जावास्क्रिप्ट में सटीक वित्तीय गणना। गोत्र क्या हैं?


125

क्रॉस-प्लेटफ़ॉर्म कोड बनाने के हित में, मैं जावास्क्रिप्ट में एक सरल वित्तीय अनुप्रयोग विकसित करना चाहूंगा। गणना के लिए आवश्यक चक्रवृद्धि ब्याज और अपेक्षाकृत लंबी दशमलव संख्या शामिल है। मैं यह जानना चाहता हूं कि इस प्रकार के गणित को करने के लिए जावास्क्रिप्ट का उपयोग करते समय किन गलतियों से बचना चाहिए - यदि यह बिल्कुल संभव है!

जवाबों:


107

आपको संभवतः अपने दशमलव मानों को 100 से बढ़ाना चाहिए, और पूरे सेंट में सभी मौद्रिक मूल्यों का प्रतिनिधित्व करना चाहिए। यह फ्लोटिंग-पॉइंट लॉजिक और अंकगणित की समस्याओं से बचने के लिए है । जावास्क्रिप्ट में कोई दशमलव डेटा प्रकार नहीं है - केवल संख्यात्मक डेटा प्रकार फ्लोटिंग-पॉइंट है। इसलिए आमतौर पर डॉलर के 2550बजाय पैसे को संभालने की सिफारिश की जाती है 25.50

जावास्क्रिप्ट में विचार करें:

var result = 1.0 + 2.0;     // (result === 3.0) returns true

परंतु:

var result = 0.1 + 0.2;     // (result === 0.3) returns false

अभिव्यक्ति 0.1 + 0.2 === 0.3लौटती है false, लेकिन फ़्लोटिंग-पॉइंट में सौभाग्य से पूर्णांक अंकगणित सटीक है, इसलिए दशमलव प्रतिनिधित्व त्रुटियों को 1 स्केलिंग से बचा जा सकता है ।

ध्यान दें कि जब वास्तविक संख्याओं का सेट अनंत होता है, तो उनमें से केवल एक परिमित संख्या (18,437,736,874,454,810,627) सटीक रूप से जावास्क्रिप्ट फ्लोटिंग-पॉइंट प्रारूप द्वारा दर्शाई जा सकती है। इसलिए अन्य नंबरों का प्रतिनिधित्व वास्तविक संख्या 2 का एक अनुमान होगा ।


1 डगलस क्रॉफोर्ड: जावास्क्रिप्ट: द गुड पार्ट्स : अपेंडिक्स ए - अव्वल पार्ट्स (पृष्ठ 105)
2 डेविड फलागन: जावास्क्रिप्ट: द डेफिनिटिव गाइड, चौथा संस्करण : 3.1.3 फ्लोटिंग-पॉइंट लिटरल्स (पृष्ठ 31)


3
और एक अनुस्मारक के रूप में, हमेशा प्रतिशत के लिए गोल गणना, और उपभोक्ता को कम से कम फायदेमंद तरीके से करें, IE यदि आप कर की गणना कर रहे हैं, तो राउंड अप करें। यदि आप अर्जित ब्याज की गणना कर रहे हैं, तो काटें।
जोश

6
@Cirrostratus: आप stackoverflow.com/questions/744099 की जाँच करना चाह सकते हैं । यदि आप स्केलिंग विधि के साथ आगे बढ़ते हैं, तो सामान्य तौर पर आप सटीक बनाए रखने की इच्छा वाले दशमलव अंकों की संख्या से अपना मान बढ़ाना चाहेंगे। यदि आपको 2 दशमलव स्थानों की आवश्यकता है, तो 100 से बड़े पैमाने पर, यदि आपको 4 की आवश्यकता है, तो 10000 से बड़े पैमाने पर।
डैनियल वैसलो

2
... 3000.57 मूल्य के बारे में, हाँ, यदि आप उस मूल्य को जावास्क्रिप्ट चर में संग्रहीत करते हैं, और आप उस पर अंकगणित करने का इरादा रखते हैं, तो आप इसे 300057 (सेंट की संख्या) स्केल करना चाहते हैं। क्योंकि 3000.57 + 0.11 === 3000.68लौटता है false
डैनियल वैसलो

7
डॉलर के बजाय पेनी की गिनती करने से मदद नहीं मिलेगी। पेनी की गिनती करते समय, आप लगभग 10 ^ 16 पर एक पूर्णांक में 1 जोड़ने की क्षमता को ढीला करते हैं। डॉलर की गिनती करते समय आप .01 को 10 ^ 14 पर संख्या में जोड़ने की क्षमता खो देते हैं। यह उसी तरह है।
slashingweapon

1
मैंने सिर्फ एक npm और Bower मॉड्यूल बनाया है, जो इस कार्य के लिए उम्मीद से मदद करना चाहिए!
पेंसिएरिनम्यूजिकला

18

हर मूल्य को 100 से पार करना ही समाधान है। इसे हाथ से करना शायद बेकार है, क्योंकि आप पुस्तकालयों को ढूंढ सकते हैं जो आपके लिए ऐसा करते हैं। मैं पैसेफ़ेयर की सलाह देता हूं, जो ES6 अनुप्रयोगों के लिए अनुकूल कार्यात्मक एपीआई प्रदान करता है:

const { in$, $ } = require('moneysafe');
console.log(in$($(10.5) + $(.3)); // 10.8

https://github.com/ericelliott/moneysafe

Node.js और ब्राउज़र दोनों में काम करता है।


2
Upvoted। "100 बाय स्केल" बिंदु पहले से ही स्वीकृत उत्तर में कवर किया गया है, हालांकि यह अच्छा है कि आपने आधुनिक जावास्क्रिप्ट सिंटैक्स के साथ एक सॉफ्टवेयर पैकेज विकल्प जोड़ा है। FWIW in$, $ मान नाम उन लोगों के लिए अस्पष्ट हैं, जिन्होंने पहले पैकेज का उपयोग नहीं किया है। मुझे पता है कि एरिक की पसंद थी कि इस तरह से चीजों को नाम दें, लेकिन मुझे अभी भी लगता है कि यह एक गलती के लिए पर्याप्त है कि मैं शायद उनका नाम बदल कर आयात / विनाशकारी बयान की आवश्यकता हूं।
james_womack

4
100 से स्केलिंग केवल तब तक मदद करती है जब तक आप गणना प्रतिशत (प्रदर्शन विभाजन, अनिवार्य रूप से) जैसे कुछ करना चाहते हैं।
पॉइन्टी

2
काश मैं एक टिप्पणी कई बार upvote कर सकता। 100 से स्केलिंग पर्याप्त नहीं है। जावास्क्रिप्ट में केवल संख्यात्मक डेटाटाइप अभी भी एक अस्थायी बिंदु डेटा प्रकार है, और आप अभी भी महत्वपूर्ण गोलाई त्रुटियों के साथ समाप्त होने जा रहे हैं।
क्रेग

1
और रीडमी से एक और सिर Money$afe has not yet been tested in production at scale.:। बस यह इंगित करना कि कोई भी व्यक्ति तब विचार कर सकता है कि क्या यह उनके उपयोग के मामले के लिए उपयुक्त है
नोबिता

7

सिर्फ दो दशमलव अंश अंकों के कारण "सटीक" वित्तीय गणना जैसी कोई चीज नहीं है, लेकिन यह एक अधिक सामान्य समस्या है।

जावास्क्रिप्ट में, आप हर मान को 100 से बढ़ा सकते हैं और उपयोग कर सकते हैं Math.round() हर बार एक अंश का कर सकते हैं।

आप संख्याओं को संग्रहीत करने और इसके प्रोटोटाइप valueOf()विधि में गोलाई को शामिल करने के लिए किसी ऑब्जेक्ट का उपयोग कर सकते हैं । ऐशे ही:

sys = require('sys');

var Money = function(amount) {
        this.amount = amount;
    }
Money.prototype.valueOf = function() {
    return Math.round(this.amount*100)/100;
}

var m = new Money(50.42355446);
var n = new Money(30.342141);

sys.puts(m.amount + n.amount); //80.76569546
sys.puts(m+n); //80.76

इस तरह, हर बार जब आप एक मनी-ऑब्जेक्ट का उपयोग करते हैं, तो इसे दो दशमलव तक गोल किया जाएगा। घिरे हुए मूल्य के माध्यम से अभी भी सुलभ है m.amount

आप अपनी खुद की गोलाई एल्गोरिथ्म में बना सकते हैं Money.prototype.valueOf(), अगर आपको पसंद है।


मुझे यह वस्तु-उन्मुख दृष्टिकोण पसंद है, यह तथ्य कि मनी ऑब्जेक्ट दोनों मूल्यों को रखता है, बहुत उपयोगी है। यह सटीक प्रकार की कार्यक्षमता है जिसे मैं अपने कस्टम ऑब्जेक्टिव-सी कक्षाओं में बनाना पसंद करता हूं।
james_womack

4
यह गोल करने के लिए पर्याप्त सटीक नहीं है।
हेनरी त्सेंग

3
Sys.puts (m + n) नहीं होना चाहिए; //80.76 वास्तव में sys.puts (m + n) पढ़ा; //80.77? मेरा मानना ​​है कि आप .5 को गोल करना भूल गए।
डेव एल

2
इस तरह के दृष्टिकोण में कई सूक्ष्म मुद्दे हैं जो फसल कर सकते हैं। उदाहरण के लिए, आपने इसके अलावा, घटाव, गुणा और इतने पर के सुरक्षित तरीके लागू नहीं किए हैं, इसलिए आपको धन राशियों के संयोजन के समय गोलाई त्रुटियों में भाग लेने की संभावना है
1800 सूचना

2
यहाँ समस्या यह है कि उदाहरण के Money(0.1)लिए जावास्क्रिप्ट लेक्सर स्रोत से स्ट्रिंग "0.1" को पढ़ता है और फिर इसे एक बाइनरी फ्लोटिंग पॉइंट में परिवर्तित करता है और फिर आपने पहले से ही अनपेक्षित राउंडिंग किया। समस्या प्रतिनिधित्व के बारे में है (बाइनरी बनाम दशमलव) सटीक के बारे में नहीं ।
एमजीडी

3

दशमलव का उपयोग करें ... यह एक बहुत अच्छा पुस्तकालय है जो समस्या का एक कठोर हिस्सा हल करता है ...

बस अपने सभी ऑपरेशन में इसका इस्तेमाल करें।

https://github.com/MikeMcl/decimal.js/


2

आपकी समस्या अस्थायी बिंदु गणना में अशुद्धि से उपजी है। यदि आप इसे हल करने के लिए केवल राउंडिंग का उपयोग कर रहे हैं, तो जब आप गुणा और विभाजन कर रहे हों तो आपको अधिक त्रुटि होगी।

समाधान नीचे है, एक स्पष्टीकरण इस प्रकार है:

इसे समझने के लिए आपको गणित के बारे में सोचना होगा। 1/3 जैसी वास्तविक संख्याओं को गणित में दशमलव मानों के साथ नहीं दर्शाया जा सकता क्योंकि वे अंतहीन हैं (जैसे - .333333333333333 ...)। दशमलव में कुछ संख्याओं को बाइनरी में सही ढंग से प्रस्तुत नहीं किया जा सकता है। उदाहरण के लिए, 0.1 को सीमित संख्या में अंकों के साथ बाइनरी में सही ढंग से प्रस्तुत नहीं किया जा सकता है।

अधिक विस्तृत विवरण के लिए यहाँ देखें: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

समाधान कार्यान्वयन पर एक नज़र डालें: http://floating-point-gui.de/languages/javascript/


1

उनके एन्कोडिंग की द्विआधारी प्रकृति के कारण, कुछ दशमलव संख्याओं का सही सटीकता के साथ प्रतिनिधित्व नहीं किया जा सकता है। उदाहरण के लिए

var money = 600.90;
var price = 200.30;
var total = price * 3;

// Outputs: false
console.log(money >= total);

// Outputs: 600.9000000000001
console.log(total);

यदि आपको शुद्ध जावास्क्रिप्ट का उपयोग करने की आवश्यकता है तो आपको हर गणना के समाधान के बारे में सोचने की आवश्यकता है। उपरोक्त कोड के लिए हम दशमलव को पूरे पूर्णांक में बदल सकते हैं।

var money = 60090;
var price = 20030;
var total = price * 3;

// Outputs: true
console.log(money >= total);

// Outputs: 60090
console.log(total);

जावास्क्रिप्ट में दशमलव गणित के साथ समस्याओं से बचना

महान प्रलेखन के साथ वित्तीय गणना के लिए एक समर्पित पुस्तकालय है। Finance.js


मुझे यह पसंद है कि Finance.js में उदाहरण एप्लिकेशन हैं
james_womack

0

दुर्भाग्य से सभी उत्तर अब तक इस तथ्य की अनदेखी करते हैं कि सभी मुद्राओं में 100 उप-इकाइयां नहीं हैं (उदाहरण के लिए, प्रतिशत अमेरिकी डॉलर (यूएसडी) की उप-इकाई है)। इराकी दिनार (IQD) जैसी मुद्राओं की 1000 उप-इकाइयाँ हैं: एक इराकी दिनार की 1000 फ़िल्में हैं। जापानी येन (जेपीवाई) की कोई उप-इकाइयाँ नहीं हैं। इसलिए "पूर्णांक अंकगणित करने के लिए 100 से गुणा करें" हमेशा सही उत्तर नहीं होता है।

इसके अलावा मौद्रिक गणना के लिए आपको मुद्रा का भी ध्यान रखना होगा। आप एक भारतीय डॉलर (INR) में एक अमेरिकी डॉलर (USD) नहीं जोड़ सकते हैं (पहले एक दूसरे को परिवर्तित किए बिना)।

अधिकतम राशि पर सीमाएं भी हैं जिन्हें जावास्क्रिप्ट के पूर्णांक डेटा प्रकार द्वारा दर्शाया जा सकता है।

मौद्रिक गणनाओं में आपको यह भी ध्यान रखना होगा कि धन में परिमित परिशुद्धता (आमतौर पर 0-3 दशमलव अंक) और गोलाई को विशेष तरीकों से करने की आवश्यकता है (उदाहरण के लिए, "सामान्य" गोलाई बनाम बैंकर की गोलाई)। प्रदर्शन करने के लिए गोलाई का प्रकार भी अधिकार क्षेत्र / मुद्रा से भिन्न हो सकता है।

कैसे जावास्क्रिप्ट में पैसे को संभालने के लिए प्रासंगिक बिंदुओं की बहुत अच्छी चर्चा है।

अपनी खोजों में मैंने दीनरो.जेएस लाइब्रेरी को पाया जो कई मुद्दों को संबोधित करता है मौद्रिक गणना । उत्पादन प्रणाली में अभी तक इसका उपयोग नहीं किया गया है, इसलिए इस पर एक सूचित राय नहीं दे सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.