කිසි විටෙකත් මුදල් double
හෝ float
වර්ග සමඟ මුදල් නියෝජනය නොකරන ලෙස මට නිතරම පවසා ඇති අතර , මේ වතාවේ මම ඔබට ප්රශ්නය අසමි: ඇයි?
ඉතා හොඳ හේතුවක් ඇති බව මට විශ්වාසයි, එය කුමක්දැයි මම නොදනිමි.
කිසි විටෙකත් මුදල් double
හෝ float
වර්ග සමඟ මුදල් නියෝජනය නොකරන ලෙස මට නිතරම පවසා ඇති අතර , මේ වතාවේ මම ඔබට ප්රශ්නය අසමි: ඇයි?
ඉතා හොඳ හේතුවක් ඇති බව මට විශ්වාසයි, එය කුමක්දැයි මම නොදනිමි.
Answers:
පාවෙන හා යුගල වශයෙන් අප මුදල් සඳහා භාවිතා කරන මූලික 10 ගුණකය නිවැරදිව නිරූපණය කළ නොහැකි බැවිනි. මෙම ගැටළුව ජාවා සඳහා පමණක් නොවේ, එය පදනම් 2 පාවෙන ලක්ෂ්ය වර්ග භාවිතා කරන ඕනෑම ක්රමලේඛන භාෂාවක් සඳහා වේ.
10 වන පාදයේ දී, ඔබට 10.25 1025 * 10 -2 ලෙස ලිවිය හැකිය (පූර්ණ සංඛ්යාවක් 10 ක බලයක් මෙන්). IEEE-754 පාවෙන ලක්ෂ්ය සංඛ්යා වෙනස් වේ, නමුත් ඒවා ගැන සිතීමට ඉතා සරල ක්රමයක් වන්නේ ඒ වෙනුවට දෙකක බලයකින් ගුණ කිරීමයි. නිදසුනක් ලෙස, ඔබ 164 * 2 -4 (පූර්ණ සංඛ්යා දෙකක බලයක් මෙන්) දෙස බැලිය හැකිය , එය 10.25 ට සමාන වේ. මතකයේ සංඛ්යා නිරූපණය වන්නේ එලෙස නොවේ, නමුත් ගණිත ඇඟවුම් සමාන වේ.
10 වන පාදයේ වුවද, මෙම අංකනයට බොහෝ සරල කොටස් නිවැරදිව නිරූපණය කළ නොහැක. උදාහරණයක් ලෙස, ඔබට 1/3 නියෝජනය කළ නොහැක: දශම නිරූපණය පුනරාවර්තනය වේ (0.3333 ...), එබැවින් 1/3 ලබා ගැනීම සඳහා ඔබට 10 ක බලයකින් ගුණ කළ හැකි සීමිත පූර්ණ සංඛ්යාවක් නොමැත. ඔබට 33 හි දිගු අනුක්රමයක් සහ 333333333 * 10 -10 වැනි කුඩා on ාතයක් මත පදිංචි විය හැකිය , නමුත් එය නිවැරදි නොවේ: ඔබ එය 3 න් ගුණ කළහොත් ඔබට 1 ක් නොලැබේ.
කෙසේ වෙතත්, මුදල් ගණනය කිරීමේ අරමුණ සඳහා, අවම වශයෙන් එක්සත් ජනපද ඩොලරයේ විශාලත්වය අනුව මුදල් වටිනාකමක් ඇති රටවල් සඳහා, සාමාන්යයෙන් ඔබට අවශ්ය වන්නේ 10 -2 ගුණක ගබඩා කිරීමට හැකි වීම පමණි , එබැවින් එය ඇත්ත වශයෙන්ම වැදගත් නොවේ 1/3 නිරූපණය කළ නොහැකි බව.
පාවෙන සහ යුගල පිළිබඳ ගැටළුව වන්නේ මුදල් වැනි සංඛ්යා වලින් බහුතරයකට පූර්ණ සංඛ්යා 2 ක බලයක් ලෙස නිශ්චිත නිරූපණයක් නොමැති වීමයි. ඇත්ත වශයෙන්ම, 0 සහ 1 අතර 0.01 හි එකම ගුණකය (ඒවා ගනුදෙනු කිරීමේදී වැදගත් වේ IEEE-754 ද්විමය පාවෙන ලක්ෂ්ය අංකයක් ලෙස නිරූපණය කළ හැකි 0, 0.25, 0.5, 0.75 සහ 1 ලෙස නිරූපණය කළ හැකි මුදල් සමඟ). අනෙක් සියල්ලම සුළු ප්රමාණයකින් අක්රීයයි. 0.333333 උදාහරණයට ප්රතිසමයක් ලෙස, ඔබ 0.1 සඳහා පාවෙන ලක්ෂ්ය අගය ගෙන 10 කින් ගුණ කළහොත් ඔබට 1 ක් නොලැබේ.
ලෙස මුදල් නියෝජනය double
හෝ float
බොහෝ විට ඉතා කුඩා දෝෂ ඔබ්බෙන් වන මෘදුකාංග වට ලෙස පළමු හොඳ බලාපොරොත්තු වනු ඇත, නමුත් ඔබ අනිශ්චිත අංක පිළිබඳ වැඩි එකතු, subtractions, ගුණ හා බෙදීම් ඉටු ලෙස, දෝෂ, උග්ර කරනු ඇති අතර එවිට ඔබට නොපෙනෙන ලෙස බව වටිනාකම් සමග අවසන් කරන්නම් නිවැරදි නොවේ. මෙමඟින් මුදල් සමඟ ගනුදෙනු කිරීම සඳහා පාවෙන හා දෙගුණයක් ප්රමාණවත් නොවන අතර, මූලික 10 බලයේ ගුණකයන් සඳහා පරිපූර්ණ නිරවද්යතාවයක් අවශ්ය වේ.
ඕනෑම භාෂාවකින් ක්රියාත්මක වන විසඳුමක් නම් ඒ වෙනුවට පූර්ණ සංඛ්යා භාවිතා කර ශත ගණන් කිරීමයි. උදාහරණයක් ලෙස, 1025 ඩොලර් 10.25 ක් වනු ඇත. භාෂා කිහිපයකම මුදල් සමඟ ගනුදෙනු කිරීම සඳහා සාදන ලද වර්ග තිබේ. අනෙක් අය අතර, ජාවාට BigDecimal
පන්තිය ඇති අතර , සී # decimal
වර්ගය ඇත.
1.0 / 10 * 10
1.0 ට සමාන නොවිය හැකිය.
බ්ලොච් වෙතින්, ජේ., Java ලදායී ජාවා, 2 වන සංස්කරණය, අයිතමය 48:
මෙම
float
සහdouble
එය ලෙස 0.1 (හෝ දහයක් වෙනත් ඕනෑම සෘණ බලය) නියෝජනය කිරීමට අපහසු නිසාය විශේෂයෙන් මූල්ය ගණනය කිරීම් සඳහා රෝගාතුර-වෙළෙඳපොළට ගැළපෙන වර්ගfloat
හෝdouble
හරියටම.උදාහරණයක් ලෙස, ඔබ සතුව ඩොලර් 1.03 ක් ඇතැයි සිතමු. ඔබට කොපමණ මුදල් ඉතිරිව තිබේද?
System.out.println(1.03 - .42);
මුද්රණය කරයි
0.6100000000000001
.මෙම ගැටලුව විසඳීම සඳහා නිවැරදි මාර්ගය භාවිතා කිරීමට
BigDecimal
,int
හෝlong
මූල්ය ගණනය කිරීම් සඳහා.
BigDecimal
සමහර අවවාද තිබුණත් (කරුණාකර දැනට පිළිගත් පිළිතුර බලන්න).
long a = 104
ඩොලර් වෙනුවට ශත වලින් භාවිතා කර ගණන් කරන්න.
BigDecimal
.
මෙය නිරවද්යතාව පිළිබඳ කාරණයක් හෝ නිරවද්යතාව පිළිබඳ කාරණයක් නොවේ. 2 වන පදනම වෙනුවට ගණනය කිරීම් සඳහා 10 වන පදනම භාවිතා කරන මිනිසුන්ගේ අපේක්ෂාවන් සපුරාලීම කාරණයකි. නිදසුනක් වශයෙන්, මූල්ය ගණනය කිරීම් සඳහා යුගල භාවිතා කිරීම ගණිතමය අර්ථයෙන් “වැරදි” පිළිතුරු සපයන්නේ නැත, නමුත් එයට පිළිතුරු සැපයිය හැකිය මූල්යමය අර්ථයෙන් අපේක්ෂා කරන දේ නොවේ.
නිමැවුමට පෙර අවසාන මොහොතේදී ඔබ ඔබේ ප්රති results ල වට කර ගත්තද, අපේක්ෂාවන්ට නොගැලපෙන යුගල භාවිතා කරමින් ඔබට ඉඳහිට ප්රති result ලයක් ලබා ගත හැකිය.
කැල්කියුලේටරයක් භාවිතා කිරීම හෝ ප්රති results ල අතින් ගණනය කිරීම හරියටම 1.40 * 165 = 231. කෙසේ වෙතත්, අභ්යන්තරව යුගල භාවිතා කිරීම, මගේ සම්පාදක / මෙහෙයුම් පද්ධති පරිසරය මත, එය 230.99999 ට ආසන්න ද්විමය අංකයක් ලෙස ගබඩා කර ඇත ... එබැවින් ඔබ එම අංකය කපා දැමුවහොත්, ඔබට 231 වෙනුවට 230 ලැබෙනු ඇත. 231 හි අපේක්ෂිත ප්රති result ලය ලබා දී ඇත. එය සත්යයකි, නමුත් වටය සෑම විටම කප්පාදු කිරීම සම්බන්ධ වේ. ඔබ භාවිතා කරන වටකුරු තාක්ෂණය කුමක් වුවත්, මේ වගේ මායිම් කොන්දේසි තවමත් පවතී. අනියම් පරීක්ෂණ හෝ නිරීක්ෂණ මගින් ඒවා බොහෝ විට සොයාගත නොහැකි තරම් දුර්ලභ ය. අපේක්ෂිත පරිදි හැසිරෙන්නේ නැති ප්රති come ල විදහා දක්වන උදාහරණ සෙවීම සඳහා ඔබට යම් කේතයක් ලිවීමට සිදු විය හැකිය.
ඔබට ළඟම ඇති සතයට යමක් වට කිරීමට අවශ්ය යැයි උපකල්පනය කරන්න. එබැවින් ඔබ ඔබේ අවසාන ප්රති result ලය ගන්න, 100 කින් ගුණ කරන්න, 0.5 එකතු කරන්න, කප්පාදු කරන්න, ඉන්පසු ප්රති result ලය 100 න් බෙදන්න. ඔබ ගබඩා කළ අභ්යන්තර අංකය 3.465 වෙනුවට 3.46499999 නම්, ඔබ එම අංකය ළඟම ඇති සතයට වට කරන විට 3.46 වෙනුවට 3.47 ලබා ගනී. නමුත් ඔබේ මූලික 10 ගණනය කිරීම් මඟින් පිළිතුර හරියටම 3.465 විය යුතු අතර, එය පැහැදිලිවම 3.47 දක්වා නොව 3.46 දක්වා විය යුතුය. මූල්යමය ගණනය කිරීම් සඳහා ඔබ දෙගුණයක් භාවිතා කරන විට සැබෑ ජීවිතයේ විටින් විට මෙවැනි දේ සිදු වේ. එය දුර්ලභ ය, එබැවින් එය බොහෝ විට ගැටලුවක් ලෙස නොදැනේ, නමුත් එය සිදු වේ.
ද්විත්ව වෙනුවට ඔබේ අභ්යන්තර ගණනය කිරීම් සඳහා ඔබ 10 වන පදනම භාවිතා කරන්නේ නම්, පිළිතුරු සැමවිටම මිනිසුන් අපේක්ෂා කරන දෙයම වේ, ඔබේ කේතයේ වෙනත් දෝෂ නොමැති බව උපකල්පනය කරයි.
Math.round(0.49999999999999994)
?
මෙම සමහර ප්රතිචාර නිසා මම කලබල වී සිටිමි. මූල්ය ගණනය කිරීම් වලදී ද්විත්ව හා පාවෙන ස්ථාන ඇති බව මම සිතමි. නියත වශයෙන්ම, භාගික නොවන මුදල් ප්රමාණයක් එකතු කිරීමේදී හා අඩු කිරීමේදී පූර්ණ සංඛ්යා පන්ති හෝ බිග් ඩෙසිමල් පන්ති භාවිතා කරන විට නිරවද්යතාව නැති නොවේ. නමුත් වඩාත් සංකීර්ණ මෙහෙයුම් සිදු කරන විට, ඔබ සංඛ්යා ගබඩා කරන්නේ කෙසේ වෙතත්, බොහෝ විට දශම ස්ථාන කිහිපයක් හෝ වැඩි ගණනක් ලබා දෙන ප්රති results ල සමඟ ඔබ අවසන් වේ. ගැටලුව වන්නේ ඔබ ප්රති .ලය ඉදිරිපත් කරන්නේ කෙසේද යන්නයි.
ඔබේ ප්රති result ලය වටකුරු හා වටකුරු අතර මායිමෙහි තිබේ නම් සහ අවසාන සතයක් ඇත්තෙන්ම වැදගත් නම්, ඔබ බොහෝ විට නරඹන්නාට පැවසිය යුත්තේ පිළිතුර ආසන්න වශයෙන් මැද - වැඩි දශම ස්ථාන පෙන්වීමෙනි.
ද්විත්ව හා තවත් බොහෝ දේ පාවෙන ගැටළුව වන්නේ ඒවා විශාල සංඛ්යා හා කුඩා සංඛ්යා ඒකාබද්ධ කිරීමට භාවිතා කරන විටය. ජාවා හි,
System.out.println(1000000.0f + 1.2f - 1000000.0f);
ප්රති results ල
1.1875
පාවෙන සහ යුගල වශයෙන් දළ වශයෙන් වේ. ඔබ බිග් ඩෙසිමල් එකක් සාදා ඉදිකිරීම්කරු වෙත පාවීමක් ලබා දෙන්නේ නම්, පාවෙන ඇත්ත වශයෙන්ම සමාන වන්නේ කුමක්දැයි ඔබට පෙනේ:
groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375
මෙය ඔබට $ 1.01 නියෝජනය කිරීමට අවශ්ය වන්නේ නැත.
ගැටළුව වන්නේ අයිඊඊඊ පිරිවිතරයට සියලු භාගයන් හරියටම නිරූපණය කිරීමට ක්රමයක් නොමැති වීමයි, ඒවායින් සමහරක් පුනරාවර්තන අතුරු කොටස් ලෙස අවසන් වන අතර එමඟින් ඔබ ආසන්න වශයෙන් දෝෂ වලින් අවසන් වේ. ගණකාධිකාරිවරු හරියටම සතයක් පිටතට පැමිණීමට කැමති නිසාත්, ඔවුන්ගේ බිල්පත ගෙවා ගනුදෙනුකරුවන් කෝපයට පත් වන නිසාත්, ගෙවීම සැකසූ පසු ඔවුන් ගෙවිය යුතු මුදල .01 වන අතර ඔවුන්ට ගාස්තුවක් අය කිරීම හෝ ඔවුන්ගේ ගිණුම වැසීමට නොහැකි වීම, භාවිතා කිරීම වඩා හොඳය ජාවා හි දශම (C # වලින්) හෝ java.math.BigDecimal වැනි නිශ්චිත වර්ග.
ඔබ වටේ ගියහොත් දෝෂය පාලනය කළ නොහැකි බව නොවේ: පීටර් ලෝරිගේ මෙම ලිපිය බලන්න . මුලින් වටේ නොයෑම පහසුය. මුදල් හසුරුවන බොහෝ යෙදුම් විශාල ගණිතයක් ඉල්ලා නොසිටින අතර, මෙහෙයුම් වලින් දේවල් එකතු කිරීම හෝ විවිධ බාල්දි සඳහා මුදල් වෙන් කිරීම ඇතුළත් වේ. පාවෙන ලක්ෂ්යය හඳුන්වා දීම සහ වටකුරු කිරීම දේවල් සංකීර්ණ කරයි.
float
, double
සහ නිශ්චිත අගයන් BigDecimal
නියෝජනය කරයි . වස්තුව පරිවර්තනය කිරීමේ කේතය නිරවද්ය මෙන්ම වෙනත් මෙහෙයුම් ද වේ. වර්ගයන්ම නිරවද්ය නොවේ.
මම අවතක්සේරු කිරීමේ අවදානමක් ඇත, නමුත් මුදල් ගණනය කිරීම් සඳහා පාවෙන ලක්ෂ්ය අංකවල නුසුදුසුකම ඉක්මවා යයි මම සිතමි. Zneak විසින් පැහැදිලි කරන ලද ද්විමය-දශම නිරූපණ නොගැලපීම මැඩපැවැත්වීම සඳහා ඔබ සෙන්ටිමීටර නිවැරදිව සිදු කරන බවටත්, වැඩ කිරීමට ප්රමාණවත් තරම් ඉලක්කම් ඇති බවත් ඔබ සහතික කරන තාක් කල්, කිසිදු ගැටළුවක් ඇති නොවනු ඇත.
එක්සෙල් හි මුදල් සමඟ ගණනය කරන පුද්ගලයින් සෑම විටම ද්විත්ව නිරවද්යතා පාවෙන භාවිතා කර ඇත (එක්සෙල් හි මුදල් වර්ගයක් නොමැත) සහ වටකුරු දෝෂ පිළිබඳව කිසිවෙකු පැමිණිලි කරන බවක් මා දැක නැත.
ඇත්ත වශයෙන්ම, ඔබ තර්කානුකූලව සිටිය යුතුය; උදා: සරල වෙබ් සාප්පුවක් කිසි විටෙකත් ද්විත්ව නිරවද්යතාවයකින් යුත් පාවෙන ගැටළු ඇති නොවනු ඇත, නමුත් ඔබ උදා: ගිණුම්කරණය හෝ විශාල (සීමා රහිත) සංඛ්යාවක් එකතු කිරීමට අවශ්ය වෙනත් දෙයක් කරන්නේ නම්, ඔබට අඩි දහයක් සහිත පාවෙන ලක්ෂ්ය අංක ස්පර්ශ කිරීමට අවශ්ය නොවනු ඇත. ධ්රැවය.
පාවෙන ලක්ෂ්ය වර්ගයට ආසන්න වශයෙන් දශම දත්ත පමණක් නිරූපණය කළ හැකි බව සත්යයක් වන අතර, ඒවා ඉදිරිපත් කිරීමට පෙර අවශ්ය නිරවද්යතාවයට එක් වටයක් සංඛ්යා කළහොත් යමෙක් නිවැරදි ප්රති .ලය ලබා ගනී. සාමාන්යයෙන්.
සාමාන්යයෙන් ද්විත්ව වර්ගයේ නිරවද්යතාව සංඛ්යා 16 ට වඩා අඩු නිසා. ඔබට වඩා හොඳ නිරවද්යතාවයක් අවශ්ය නම් එය සුදුසු වර්ගයක් නොවේ. දළ විශ්ලේෂණයන් ද රැස් කර ගත හැකිය.
ඔබ ස්ථාවර ලක්ෂ්ය ගණිතයක් භාවිතා කළත් ඔබට තවමත් සංඛ්යා වට කළ යුතු බව පැවසිය යුතුය. ඔබ වරින් වර දශම සංඛ්යා ලබා ගන්නේ නම් බිග් ඉන්ටෙගර් සහ බිග් ඩෙසිමල් දෝෂ ලබා දෙයි. එබැවින් මෙහි දළ විශ්ලේෂණයක් ද ඇත.
උදාහරණයක් ලෙස මූල්ය ගණනය කිරීම් සඳහා histor තිහාසිකව භාවිතා කරන COBOL හි උපරිම නිරවද්යතාව සංඛ්යා 18 කි. එබැවින් බොහෝ විට ව්යංග වටරවුමක් ඇත.
නිගමනය නම්, දෙගුණයක් එහි ඉලක්කම් 16 ක නිරවද්යතාවයට බොහෝ දුරට නුසුදුසු වන අතර එය ප්රමාණවත් නොවිය හැකිය, එය ආසන්න වශයෙන් නිසා නොවේ.
පසුකාලීන වැඩසටහනේ පහත ප්රතිදානය සලකා බලන්න. එය පෙන්වන්නේ ද්විත්ව වටයකින් පසු බිග් ඩෙසිමල්ගේ ප්රති result ලය නිරවද්යතාව 16 දක්වා ලබා දෙන බවයි.
Precision 14
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.000051110111115611
Double : 56789.012345 / 1111111111 = 0.000051110111115611
Precision 15
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.0000511101111156110
Double : 56789.012345 / 1111111111 = 0.0000511101111156110
Precision 16
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.00005111011111561101
Double : 56789.012345 / 1111111111 = 0.00005111011111561101
Precision 17
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.000051110111115611011
Double : 56789.012345 / 1111111111 = 0.000051110111115611013
Precision 18
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.0000511101111156110111
Double : 56789.012345 / 1111111111 = 0.0000511101111156110125
Precision 19
------------------------------------------------------
BigDecimalNoRound : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal : 56789.012345 / 1111111111 = 0.00005111011111561101111
Double : 56789.012345 / 1111111111 = 0.00005111011111561101252
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.MathContext;
public class Exercise {
public static void main(String[] args) throws IllegalArgumentException,
SecurityException, IllegalAccessException,
InvocationTargetException, NoSuchMethodException {
String amount = "56789.012345";
String quantity = "1111111111";
int [] precisions = new int [] {14, 15, 16, 17, 18, 19};
for (int i = 0; i < precisions.length; i++) {
int precision = precisions[i];
System.out.println(String.format("Precision %d", precision));
System.out.println("------------------------------------------------------");
execute("BigDecimalNoRound", amount, quantity, precision);
execute("DoubleNoRound", amount, quantity, precision);
execute("BigDecimal", amount, quantity, precision);
execute("Double", amount, quantity, precision);
System.out.println();
}
}
private static void execute(String test, String amount, String quantity,
int precision) throws IllegalArgumentException, SecurityException,
IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
Method impl = Exercise.class.getMethod("divideUsing" + test, String.class,
String.class, int.class);
String price;
try {
price = (String) impl.invoke(null, amount, quantity, precision);
} catch (InvocationTargetException e) {
price = e.getTargetException().getMessage();
}
System.out.println(String.format("%-30s: %s / %s = %s", test, amount,
quantity, price));
}
public static String divideUsingDoubleNoRound(String amount,
String quantity, int precision) {
// acceptance
double amount0 = Double.parseDouble(amount);
double quantity0 = Double.parseDouble(quantity);
//calculation
double price0 = amount0 / quantity0;
// presentation
String price = Double.toString(price0);
return price;
}
public static String divideUsingDouble(String amount, String quantity,
int precision) {
// acceptance
double amount0 = Double.parseDouble(amount);
double quantity0 = Double.parseDouble(quantity);
//calculation
double price0 = amount0 / quantity0;
// presentation
MathContext precision0 = new MathContext(precision);
String price = new BigDecimal(price0, precision0)
.toString();
return price;
}
public static String divideUsingBigDecimal(String amount, String quantity,
int precision) {
// acceptance
BigDecimal amount0 = new BigDecimal(amount);
BigDecimal quantity0 = new BigDecimal(quantity);
MathContext precision0 = new MathContext(precision);
//calculation
BigDecimal price0 = amount0.divide(quantity0, precision0);
// presentation
String price = price0.toString();
return price;
}
public static String divideUsingBigDecimalNoRound(String amount, String quantity,
int precision) {
// acceptance
BigDecimal amount0 = new BigDecimal(amount);
BigDecimal quantity0 = new BigDecimal(quantity);
//calculation
BigDecimal price0 = amount0.divide(quantity0);
// presentation
String price = price0.toString();
return price;
}
}
පාවෙන ලක්ෂ්ය අංකයේ ප්රති result ලය නිශ්චිත නොවේ, එමඟින් නිශ්චිත ප්රති result ල අවශ්ය වන සහ ආසන්න වශයෙන් නොව ඕනෑම මූල්ය ගණනය කිරීමකට නුසුදුසු වේ. float සහ double සැලසුම් කර ඇත්තේ ඉංජිනේරු සහ විද්යාත්මක ගණනය කිරීම් සඳහා වන අතර බොහෝ විට නිශ්චිත ප්රති result ල නොලැබේ. පාවෙන ලක්ෂ්ය ගණනය කිරීමේ ප්රති result ලය JVM සිට JVM දක්වා වෙනස් විය හැකිය. මුදල් වටිනාකම නිරූපණය කිරීම සඳහා භාවිතා කරන බිග් ඩෙසිමල් සහ ද්විත්ව ප්රාථමික පිළිබඳ පහත උදාහරණය දෙස බලන්න, පාවෙන ලක්ෂ්ය ගණනය කිරීම හරියටම නොවිය හැකි බවත් මූල්ය ගණනය කිරීම් සඳහා යමෙකු බිග් ඩෙසිමල් භාවිතා කළ යුතු බවත් පැහැදිලිය.
// floating point calculation
final double amount1 = 2.0;
final double amount2 = 1.1;
System.out.println("difference between 2.0 and 1.1 using double is: " + (amount1 - amount2));
// Use BigDecimal for financial calculation
final BigDecimal amount3 = new BigDecimal("2.0");
final BigDecimal amount4 = new BigDecimal("1.1");
System.out.println("difference between 2.0 and 1.1 using BigDecimal is: " + (amount3.subtract(amount4)));
ප්රතිදානය:
difference between 2.0 and 1.1 using double is: 0.8999999999999999
difference between 2.0 and 1.1 using BigDecimal is: 0.9
double
ෆෙඩරල් පක්ෂයට දශාංශික ඇත වත් ලෙස සියයට ෆෙඩරල් පක්ෂයට කිසිදු කරදර 0.5 සියයට ගණනය සිදු වනු ඇත. පාවෙන ලක්ෂ්ය ගණනය කිරීම් මඟින් උදා: 123.499941 of ක පොලී අගයක් ලබා දෙන්නේ නම්, ද්විමය එෆ්පී හෝ දශම එෆ්පී හරහා නම්, ද්විත්ව වටකුරු ගැටළුව සමාන වේ - කිසිදු ආකාරයකින් වාසියක් නැත. ඔබේ පරිශ්රය ගණිතමය වශයෙන් නිවැරදි අගය උපකල්පනය කරන අතර දශම FP එක සමාන වේ - දශම FP පවා සහතික නොකරයි. 0.5 / 7.0 * 7.0 යනු ද්විමය හා ඩයික්මාල් එෆ්පී සඳහා ගැටළුවකි. අයිඒසී, සී හි ඊළඟ අනුවාදය දශම එෆ්පී ලබා දෙනු ඇතැයි මා අපේක්ෂා කරන පරිදි බොහෝමයක් වෙනස් වනු ඇත.
කලින් පවසා ඇති පරිදි, මෘදුකාංගය කුඩා දෝෂයන්ගෙන් මිදෙන විට මුලින් මුදල් දෙගුණයක් හෝ පාවීමක් ලෙස නිරූපණය කිරීම හොඳ වනු ඇත, නමුත් ඔබ නිරවද්ය සංඛ්යා මත තවත් එකතු කිරීම්, අඩු කිරීම්, ගුණ කිරීම් සහ බෙදීම් සිදු කරන විට, ඔබට වැඩි වැඩියෙන් නිරවද්යතාවය අහිමි වේ දෝෂ එකතු වන විට, මෙය මුදල් සමඟ ගනුදෙනු කිරීම සඳහා පාවෙන හා දෙගුණයක් ප්රමාණවත් නොවන අතර, මූලික 10 බලයේ ගුණකයන් සඳහා පරිපූර්ණ නිරවද්යතාවයක් අවශ්ය වේ.
අවසාන වශයෙන් ජාවාට මුදල් හා මුදල් සමඟ වැඩ කිරීමට සම්මත ක්රමයක් තිබේ!
JSR 354: මුදල් සහ මුදල් API
JSR 354 මුදල් සහ මුදල් සමඟ සවිස්තරාත්මක ගණනය කිරීම් නියෝජනය කිරීම, ප්රවාහනය කිරීම සහ සිදු කිරීම සඳහා API සපයයි. ඔබට මෙම සබැඳියෙන් එය බාගත හැකිය:
JSR 354: මුදල් සහ මුදල් API බාගත කිරීම
පිරිවිතර පහත සඳහන් දේවලින් සමන්විත වේ:
- උදා: මුදල් හා මුදල් හැසිරවීමේ API
- හුවමාරු කළ හැකි ක්රියාත්මක කිරීම් සඳහා සහාය වන API
- ක්රියාත්මක කිරීමේ පන්තිවල අවස්ථා නිර්මාණය කිරීම සඳහා කර්මාන්තශාලා
- මුදල් ප්රමාණයන් ගණනය කිරීම, පරිවර්තනය කිරීම සහ හැඩතල ගැන්වීම සඳහා ක්රියාකාරීත්වය
- ජාවා 9 හි ඇතුළත් කිරීමට සැලසුම් කර ඇති මුදල් සහ මුදල් සමඟ වැඩ කිරීම සඳහා ජාවා ඒපීඅයි.
- සියලුම පිරිවිතර පන්ති සහ අතුරුමුහුණත් javax.money හි පිහිටා ඇත. * පැකේජය.
JSR 354 හි නියැදි උදාහරණ: මුදල් සහ මුදල් API:
MonitAmount නිර්මාණය කර එය කොන්සෝලය වෙත මුද්රණය කිරීමේ උදාහරණයක් ලෙස පෙනේ:
MonetaryAmountFactory<?> amountFactory = Monetary.getDefaultAmountFactory();
MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));
යොමු ක්රියාත්මක කිරීමේ API භාවිතා කරන විට, අවශ්ය කේතය වඩාත් සරල ය:
MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));
API ද මුදල් ගණනය කිරීම් සමඟ ගණනය කිරීම් සඳහා සහය දක්වයි:
MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));
මුදල් ඒකකය සහ මුදල් මුදල
// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);
පවරන ලද මුදල්, සංඛ්යාත්මක මුදල, එහි නිරවද්යතාවය සහ තවත් දේ වෙත ප්රවේශ වීමට ඉඩ සලසන විවිධ ක්රම මූල්ය අණු සතුව ඇත:
MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();
int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5
// NumberValue extends java.lang.Number.
// So we assign numberValue to a variable of type Number
Number number = numberValue;
වටකුරු ක්රියාකරුවෙකු භාවිතයෙන් මුදල් ගණනය කළ හැකිය:
CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35
MonitiAmounts එකතු කිරීම සමඟ වැඩ කරන විට, පෙරීම, වර්ග කිරීම සහ කණ්ඩායම් කිරීම සඳහා හොඳ උපයෝගීතා ක්රම කිහිපයක් තිබේ.
List<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));
අභිරුචි මූල්ය ගිණුම් මෙහෙයුම්
// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {
BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);
BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));
return Money.of(tenPercent, amount.getCurrency());
};
MonetaryAmount dollars = Money.of(12.34567, "USD");
// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567
සම්පත්:
JSR 354 සමඟ ජාවා හි මුදල් සහ මුදල් හැසිරවීම
ජාවා 9 මුදල් හා මුදල් ඒපීඅයි (ජේඑස්ආර් 354) සොයා බැලීම
මෙයද බලන්න: JSR 354 - මුදල් සහ මුදල්
ඔබේ ගණනය කිරීම සඳහා විවිධ පියවරයන් ඇතුළත් නම්, අත්තනෝමතික නිරවද්ය ගණිතය ඔබව 100% ක් ආවරණය නොකරයි.
ප්රති results ලවල පරිපූර්ණ නිරූපණයක් භාවිතා කිරීමට ඇති එකම විශ්වාසදායක ක්රමය (බෙදීම් මෙහෙයුම් අවසාන පියවර දක්වා කාණ්ඩගත කරන අභිරුචි භාග දත්ත වර්ගයක් භාවිතා කරන්න) සහ අවසාන පියවරේදී දශම අංකනය බවට පරිවර්තනය කිරීම පමණි.
අත්තනෝමතික නිරවද්යතාව උදව් නොවනු ඇත, මන්ද සෑම විටම බොහෝ දශම ස්ථාන ඇති සංඛ්යා තිබිය හැකි නිසා හෝ සමහර ප්රති results ල වැනි 0.6666666
... අත්තනෝමතික නියෝජනයක් අවසාන උදාහරණය ආවරණය නොකරයි. එබැවින් සෑම පියවරකදීම ඔබට කුඩා දෝෂ ඇතිවේ.
මෙම දෝෂ එකතු කිරීම, අවසානයේදී නොසලකා හැරීම පහසු නොවනු ඇත. මෙය දෝෂ ප්රචාරණය ලෙස හැඳින්වේ .
බොහෝ පිළිතුරු මගින් මුදල් හා මුදල් ගණනය කිරීම් සඳහා දෙගුණයක් භාවිතා නොකිරීමට හේතු පෙන්වා දී ඇත. මම ඔවුන් සමඟ සම්පූර්ණයෙන්ම එකඟ වෙමි.
ඒ සඳහා දෙවරක් භාවිතා කළ නොහැකි බව එයින් අදහස් නොවේ.
මම ඉතා අඩු gc අවශ්යතා සහිත ව්යාපෘති ගණනාවක වැඩ කර ඇති අතර, BigDecimal වස්තු තිබීම එම පොදු කාර්යයට විශාල දායකත්වයක් සපයයි.
මෙම නැණවත් යෝජනාව ගෙන එන ද්විත්ව නිරූපණය පිළිබඳ අවබෝධය නොමැතිකම සහ නිරවද්යතාව සහ නිරවද්යතාව හැසිරවීමේ අත්දැකීම් නොමැතිකම ය.
ඔබේ ව්යාපෘතියේ නිරවද්යතාව සහ නිරවද්යතා අවශ්යතා හසුරුවා ගැනීමට ඔබට හැකි නම් ඔබට එය ක්රියාත්මක කළ හැකිය, එය කළ යුත්තේ එක් ද්විත්ව අගයන් පරාසයක් සමඟ ගනුදෙනු කිරීමෙනි.
වැඩි අදහසක් ලබා ගැනීම සඳහා ඔබට guava හි FuzzyCompare ක්රමය වෙත යොමු විය හැකිය. පරාමිති ඉවසීම යතුරයි. සුරැකුම්පත් වෙළඳ යෙදුමක් සඳහා අපි මෙම ගැටළුව සමඟ කටයුතු කළ අතර විවිධ පරාසයන්හි විවිධ සංඛ්යාත්මක අගයන් සඳහා භාවිතා කළ යුතු ඉවසීම් මොනවාද යන්න පිළිබඳව අපි පරිපූර්ණ පර්යේෂණයක් කළෙමු.
එසේම, හැෂ් සිතියම ක්රියාත්මක කිරීම සමඟ සිතියම් යතුරක් ලෙස ද්විත්ව එතීම් භාවිතා කිරීමට ඔබ පෙළඹෙන අවස්ථාද තිබිය හැකිය. එය ඉතා අවදානම් සහගත බැවින් "0.5" සහ "0.6 - 0.1" අගයන් සඳහා Double.equals සහ හැෂ් කේතය විශාල අවුලක් ඇති කරයි.
මෙම ප්රශ්නයට පළ කරන ලද බොහෝ පිළිතුරු IEEE සහ පාවෙන ලක්ෂ්ය අංක ගණිතය වටා ඇති ප්රමිතීන් සාකච්ඡා කරයි.
පරිගණක නොවන විද්යා පසුබිමකින් (භෞතික විද්යාව හා ඉංජිනේරු විද්යාව) මම වෙනත් දෘෂ්ටිකෝණයකින් ගැටලු දෙස බැලීමට නැඹුරු වෙමි. මට නම්, මම ගණිතමය ගණනය කිරීමක දී දෙගුණයක් හෝ පාවීමක් භාවිතා නොකිරීමට හේතුව මට ඕනෑවට වඩා තොරතුරු නැති වීමයි.
විකල්ප මොනවාද? බොහෝ දේ ඇත (සහ තවත් බොහෝ දේ මම නොදනිමි!).
ජාවා හි බිග් ඩෙසිමල් ජාවා භාෂාවට ආවේණිකය. Apfloat යනු ජාවා සඳහා තවත් අත්තනෝමතික-නිරවද්ය පුස්තකාලයකි.
C # හි ඇති දශම දත්ත වර්ගය මයික්රොසොෆ්ට් හි .NET විකල්පයයි.
SciPy (Scientific Python) හට බොහෝ විට මූල්ය ගණනය කිරීම් ද කළ හැකිය (මම උත්සාහ කර නැත, නමුත් මම එසේ සැක කරමි).
GNU බහු නිරවද්ය පුස්තකාලය (GMP) සහ GNU MFPR පුස්තකාලය යනු C සහ C ++ සඳහා නිදහස් හා විවෘත මූලාශ්ර සම්පත් දෙකකි.
ජාවාස්ක්රිප්ට් (!) සඳහා සංඛ්යාත්මක නිරවද්යතා පුස්තකාල ද ඇති අතර මූල්ය ගණනය කිරීම් හැසිරවිය හැකි PHP ලෙස මම සිතමි.
බොහෝ පරිගණක භාෂා සඳහා හිමිකාරීත්වය (විශේෂයෙන්, ෆෝට්රාන් සඳහා) සහ විවෘත-මූලාශ්ර විසඳුම් ද ඇත.
මම පරිගණක විද්යා scient යෙක් නොවේ. කෙසේ වෙතත්, මම ජාවා හි බිග් ඩෙසිමල් හෝ සී # හි දශම දෙසට නැඹුරු වෙමි. මම ලැයිස්තුගත කර ඇති වෙනත් විසඳුම් මම උත්සාහ කර නැත, නමුත් ඒවා ද ඉතා හොඳ ය.
මට නම්, මම BigDecimal වලට කැමතියි එය සහාය දක්වන ක්රම නිසා. සී # දශම ඉතා හොඳයි, නමුත් මට කැමති තරම් එය සමඟ වැඩ කිරීමට මට අවස්ථාවක් ලැබී නැත. මගේ විවේක කාලය තුළ මා උනන්දුවක් දක්වන විද්යාත්මක ගණනය කිරීම් සිදු කරන අතර, බිග් ඩෙසිමල් ඉතා හොඳින් ක්රියා කරන බව පෙනේ, මන්ද මගේ පාවෙන ලක්ෂ්ය සංඛ්යා වල නිරවද්යතාවය සැකසිය හැකි බැවිනි. බිග් ඩෙසිමල් හි අවාසිය? සමහර විට එය බෙදීමේ ක්රමය භාවිතා කරන්නේ නම් එය මන්දගාමී විය හැකිය.
ඔබට සී, සී ++ සහ ෆෝට්රාන් හි නිදහස් හා හිමිකාර පුස්තකාල සොයා බැලිය හැකිය.
පෙර පිළිතුරු එකතු කිරීම සඳහා, ප්රශ්නයේ දී විසඳන ලද ගැටලුව සමඟ කටයුතු කිරීමේදී බිග් ඩෙසිමල් හැරුණු විට ජෝඩා -මුදල් ජාවා හි ක්රියාත්මක කිරීමේ විකල්පයක් ද ඇත. ජාවා මොඩියුලයේ නම org.joda.money.
එයට ජාවා එස්ඊ 8 හෝ ඊට පසුව අවශ්ය වන අතර කිසිදු පරායත්තතාවයක් නොමැත.
වඩාත් නිවැරදිව කිවහොත්, සම්පාදක-කාල පරායත්තතාවයක් ඇති නමුත් එය අවශ්ය නොවේ.
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
ජෝඩා මුදල් භාවිතා කිරීමේ උදාහරණ:
// create a monetary value
Money money = Money.parse("USD 23.87");
// add another amount with safe double conversion
CurrencyUnit usd = CurrencyUnit.of("USD");
money = money.plus(Money.of(usd, 12.43d));
// subtracts an amount in dollars
money = money.minusMajor(2);
// multiplies by 3.5 with rounding
money = money.multipliedBy(3.5d, RoundingMode.DOWN);
// compare two amounts
boolean bigAmount = money.isGreaterThan(dailyWage);
// convert to GBP using a supplied rate
BigDecimal conversionRate = ...; // obtained from code outside Joda-Money
Money moneyGBP = money.convertedTo(CurrencyUnit.GBP, conversionRate, RoundingMode.HALF_UP);
// use a BigMoney for more complex calculations where scale matters
BigMoney moneyCalc = money.toBigMoney();
ප්රලේඛනය: http://joda-money.sourceforge.net/apidocs/org/joda/money/Money.html
ක්රියාත්මක කිරීමේ උදාහරණ: https://www.programcreek.com/java-api-examples/?api=org.joda.money.Money
සමහර උදාහරණ ... මෙය ඕනෑම ක්රමලේඛන භාෂාවකින් ක්රියා කරයි (ඇත්ත වශයෙන්ම බලාපොරොත්තු වූ පරිදි ක්රියා නොකරයි) ... මම ඩෙල්ෆි, වීබීඑස්ක්රිප්ට්, විෂුවල් බේසික්, ජාවාස්ක්රිප්ට් සහ දැන් ජාවා / ඇන්ඩ්රොයිඩ් සමඟ උත්සාහ කර ඇත:
double total = 0.0;
// do 10 adds of 10 cents
for (int i = 0; i < 10; i++) {
total += 0.1; // adds 10 cents
}
Log.d("round problems?", "current total: " + total);
// looks like total equals to 1.0, don't?
// now, do reverse
for (int i = 0; i < 10; i++) {
total -= 0.1; // removes 10 cents
}
// looks like total equals to 0.0, don't?
Log.d("round problems?", "current total: " + total);
if (total == 0.0) {
Log.d("round problems?", "is total equal to ZERO? YES, of course!!");
} else {
Log.d("round problems?", "is total equal to ZERO? NO... thats why you should not use Double for some math!!!");
}
නිමැවුම්:
round problems?: current total: 0.9999999999999999
round problems?: current total: 2.7755575615628914E-17
round problems?: is total equal to ZERO? NO... thats why you should not use Double for some math!!!
පාවීම යනු විවිධ මෝස්තර සහිත දශමයේ ද්විමය ආකාරයකි; ඒවා වෙනස් කරුණු දෙකකි. එකිනෙකට පරිවර්තනය කිරීමේදී වර්ග දෙකක් අතර සුළු දෝෂ තිබේ. එසේම, පාවීම නිර්මාණය කර ඇත්තේ විද්යාත්මක සඳහා අනන්ත විශාල අගයන් නිරූපණය කිරීම සඳහා ය. ඒ කියන්නේ එය නිර්මාණය කර ඇත්තේ එම ස්ථාවර බයිට් ගණන සමඟ අතිශය කුඩා හා අන්ත විශාල සංඛ්යාවට නිරවද්යතාව නැති කර ගැනීම සඳහා ය. දශමයට අසීමිත අගයන් සංඛ්යාවක් නිරූපණය කළ නොහැක, එය එම දශම සංඛ්යා ගණනට සීමා වේ. එබැවින් ෆ්ලෝට් සහ දශම විවිධ අරමුණු සඳහා වේ.
මුදල් වටිනාකම සඳහා දෝෂය කළමනාකරණය කිරීමට ක්රම කිහිපයක් තිබේ:
දිගු නිඛිලයක් භාවිතා කර ඒ වෙනුවට ශත වලින් ගණන් කරන්න.
ද්විත්ව නිරවද්යතාවය භාවිතා කරන්න, ඔබේ සැලකිය යුතු ඉලක්කම් 15 දක්වා තබා ගන්න එවිට දශම හරියටම අනුකරණය කළ හැකිය. අගයන් ඉදිරිපත් කිරීමට පෙර වටය; ගණනය කිරීම් සිදු කරන විට බොහෝ විට වටය.
ජාවා බිග් ඩෙසිමල් වැනි දශම පුස්තකාලයක් භාවිතා කරන්න, එවිට ඔබට දශම අනුකරණය කිරීමට දෙවරක් භාවිතා කිරීමට අවශ්ය නොවේ.
බොහෝ වෙළඳ නාම අතින් ගෙන යා හැකි විද්යාත්මක කැල්කියුලේටරයන් පාවීම වෙනුවට දශමයෙන් ක්රියා කරන බව දැන ගැනීම සිත්ගන්නා කරුණකි. එබැවින් කිසිවෙකු පාවෙන පරිවර්තන දෝෂ පැමිණිලි නොකරයි.
ඇමරිකානු මුදල් පහසුවෙන් ඩොලර් හා සත ප්රමාණයෙන් නිරූපණය කළ හැකිය. පූර්ණ සංඛ්යා 100% නිරවද්ය වන අතර, පාවෙන ලක්ෂ්ය ද්විමය සංඛ්යා හරියටම පාවෙන ලක්ෂ්ය දශමයට නොගැලපේ.