මෙම ප්රශ්නය සංකීර්ණයි.
අපට ශ්රිතයක් ඇතැයි සිතමු roundTo2DP(num)
, එය පාවීමක් තර්කයක් ලෙස ගෙන වටකුරු අගයක් දශමස්ථාන 2 ක් වෙත ලබා දෙයි. මෙම එක් එක් ප්රකාශනය ඇගයීමට ලක් කළ යුත්තේ කුමක් ද?
roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
'පැහැදිලිව පෙනෙන' පිළිතුර නම්, පළමු උදාහරණය 0.01 දක්වා විය යුතු අතර (එය 0.02 ට වඩා 0.01 ට ආසන්න බැවින්) අනෙක් දෙක 0.02 ට වට විය යුතුය (මන්ද 0.0150000000000000001 0.01 ට වඩා 0.02 ට ආසන්න වන අතර 0.015 හරියටම අඩක් අතර ඇති නිසා ඒවා සංඛ්යාත්මකව ගණනය කරන ගණිතමය සම්මුතියක් ඇත).
ඔබ අනුමාන කළ අල්ලා ගැනීම නම්, එම පැහැදිලි පිළිතුරු ලබා දීම සඳහා එය ක්රියාත්මක roundTo2DP
කළ නොහැකි වීමයි, මන්ද එයට ලබා දුන් අංක තුනම එකම සංඛ්යාවක් වන බැවිනි. අයිඊඊඊ 754 ද්විමය පාවෙන ලක්ෂ්ය සංඛ්යා (ජාවාස්ක්රිප්ට් භාවිතා කරන ආකාරයේ) බොහෝ පූර්ණ සංඛ්යා නොවන සංඛ්යා නිරූපණය කළ නොහැක, එබැවින් ඉහත සංඛ්යාත්මක වචනාර්ථ තුනම අසල ඇති වලංගු පාවෙන ලක්ෂ්ය අංකයකට රවුම් වේ. මෙම අංකය, එය සිදු වන විට, හරියටම වේ
0.01499999999999999944488848768742172978818416595458984375
එය 0.02 ට වඩා 0.01 ට ආසන්න වේ.
ඔබගේ බ්රව්සර් කොන්සෝලය, නෝඩ් ෂෙල් හෝ වෙනත් ජාවාස්ක්රිප්ට් පරිවර්තකයේදී අංක තුනම එක හා සමාන බව ඔබට දැක ගත හැකිය. ඒවා සංසන්දනය කරන්න:
> 0.014999999999999999 === 0.0150000000000000001
true
ඒ නිසා මම ලියන විට m = 0.0150000000000000001
, මා අවසන් කරන නිශ්චිත වටිනාකමm
ඊට වඩා සමීප 0.01
වේ 0.02
. තවමත්, මම නූල් බවට පරිවර්තනය කළහොත් m
...
> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015
... මට 0.015 ක් ලැබෙන අතර එය 0.02 දක්වා වට විය යුතු අතර එය සැලකිය යුතු නොවේ 56 දශම-ස්ථාන අංකය මා කලින් කීවේ මෙම සංඛ්යා සියල්ලම හරියටම සමාන බවයි. ඉතින් මොන අඳුරු මැජික්ද?
7.1.12.1 වගන්තියේ ECMAScript පිරිවිතරයෙන් පිළිතුර සොයාගත හැකිය : අංක වර්ගයට අදාළ වන ToString . මෙහි යම් සංඛ්යාවක් m නූලක් බවට පරිවර්තනය කිරීමේ නීති රීති දක්වා ඇත. ප්රධාන කොටස 5 වන ලක්ෂ්යය වන අතර, පූර්ණ සංඛ්යා s ජනනය වන අතර එහි ඉලක්කම් m හි සංගීත නිරූපණය සඳහා භාවිතා වේ .
ඉඩ n , k , සහ ගේ එවැනි නිඛිල විය k ≥ 1, 10 k -1 ≤ ගේ <10 k , සඳහා අංකය වටිනාකම ගේ × 10 n - k යනු මීටර් , හා k හැකි තරම් කුඩා වේ. K හි දශම නිරූපණය දී ඉලක්කම් සංඛ්යාව බව සටහන ගේ බව, ගේ 10 බෙදිය නොහැකි වන අතර, අවම ලෙස සැලකිය යුතු ඉලක්කම් බව ගේ අවශ්යයෙන්ම විශිෂ්ඨ ලෙස මෙම උපමානයන් හා තීරණය කර නැත.
මෙහි ප්රධාන කොටස වන්නේ " k හැකි තරම් කුඩා" අවශ්යතාවයයි. එම අවශ්යතාවයට සමාන වන්නේ අවශ්යතාවයක් වන අතර, අංකයක් ලබා දී ඇති විට m
, එම අවශ්යතාවය සපුරාලන අතරම එහි අගය අවම සංඛ්යා සංඛ්යාවක්String(m)
තිබිය යුතුය . අප එය දැනටමත් දන්නා බැවින් , සත්ය විය යුත්තේ මන්දැයි දැන් පැහැදිලි ය.Number(String(m)) === m
0.015 === 0.0150000000000000001
String(0.0150000000000000001) === '0.015'
ඇත්ත වශයෙන්ම, මෙම සාකච්ඡාවෙන් කිසිවක් ආපසු පැමිණිය roundTo2DP(m)
යුතු දේට සෘජුවම පිළිතුරු දී නැත . නම් m
ගේ නිවැරදි වටිනාකම 0,01499999999999999944488848768742172978818416595458984375, නමුත් එහි සංගීත නිරූපණය '0,015' නම් කුමක් වේ නිවැරදි ගණිතමය, ප්රායෝගිකව, දාර්ශනික, හෝ ඕනෑම දෙයක් - - අපි දශම ස්ථාන දෙකක් එය අවට විට පිළිතුර?
මේ සඳහා නිවැරදි පිළිතුරක් නොමැත. එය ඔබගේ භාවිත අවස්ථාව මත රඳා පවතී. ඔබට බොහෝ විට සංගීත නිරූපණයට ගරු කිරීමට අවශ්ය වන අතර ඉහළට වටයන්නේ:
- නිරූපණය වන අගය සහජයෙන්ම විවික්ත වේ, උදා: ඩිනාර් වැනි දශම 3 ක ස්ථානයක මුදල් ප්රමාණයකි. මේ අවස්ථාවේ දී, සැබෑ 0,015 වැනි අංකය වටිනාකම වේ 0,015, සහ 0,0149999999 ... එය ද්විමය ඉපිලුම් ලක්ෂ්ය දී ලැබෙන බව නියෝජනය වන වරදක් යයි වරදවා වටහා වේ. (ඇත්ත වශයෙන්ම, එවැනි අගයන් හැසිරවීම සඳහා ඔබ දශම පුස්තකාලයක් භාවිතා කළ යුතු බවත්, ඒවා කිසි විටෙකත් ද්විමය පාවෙන ලක්ෂ්ය අංක ලෙස නිරූපණය නොකරන බවත් බොහෝ දෙනා තර්ක කරනු ඇත.)
- අගය පරිශීලකයෙකු විසින් ටයිප් කරන ලදි. මෙම අවස්ථාවේ දී, නැවතත්, ඇතුළත් කළ නිශ්චිත දශම අංකය ආසන්නතම ද්විමය පාවෙන ලක්ෂ්ය නිරූපණයට වඩා 'සත්ය' වේ.
අනෙක් අතට, ඔබේ වටිනාකම සහජයෙන්ම අඛණ්ඩ පරිමාණයකින් වන විට ද්විමය පාවෙන ලක්ෂ්ය අගයට ගරු කිරීමට සහ පහළට වටයන්නට ඔබට අවශ්ය වනු ඇත - නිදසුනක් ලෙස, එය සංවේදකයකින් කියවීමක් නම්.
මෙම ප්රවේශයන් දෙක සඳහා විවිධ කේත අවශ්ය වේ. අංකයේ සංගීත නිරූපණයට ගරු කිරීම සඳහා, අපට (තරමක් දුරට සියුම් කේතයක් සහිතව) අපගේම වටකුරු ක්රියාවට නැංවිය හැකිය, එය කෙලින්ම සංගීත නිරූපණය මත ක්රියා කරයි, ඉලක්කම් අනුව ඉලක්කම්, ඔබ පාසලේදී භාවිතා කළ ඇල්ගොරිතම භාවිතා කරමින් අංක රවුම් කරන ආකාරය උගන්වන ලදී. පහත දැක්වෙන්නේ දශමස්ථානයෙන් පසු පසුපස බිංදුව ඉවත් කිරීමෙන් “අවශ්ය විට පමණක්” සංඛ්යාව දශම ස්ථාන 2 ක් නියෝජනය කිරීමේ OP හි අවශ්යතාවයට ගරු කරන උදාහරණයකි; ඇත්ත වශයෙන්ම, ඔබට එය ඔබගේ නිශ්චිත අවශ්යතාවන්ට වෙනස් කිරීමට අවශ්ය විය හැකිය.
/**
* Converts num to a decimal string (if it isn't one already) and then rounds it
* to at most dp decimal places.
*
* For explanation of why you'd want to perform rounding operations on a String
* rather than a Number, see http://stackoverflow.com/a/38676273/1709587
*
* @param {(number|string)} num
* @param {number} dp
* @return {string}
*/
function roundStringNumberWithoutTrailingZeroes (num, dp) {
if (arguments.length != 2) throw new Error("2 arguments required");
num = String(num);
if (num.indexOf('e+') != -1) {
// Can't round numbers this large because their string representation
// contains an exponent, like 9.99e+37
throw new Error("num too large");
}
if (num.indexOf('.') == -1) {
// Nothing to do
return num;
}
var parts = num.split('.'),
beforePoint = parts[0],
afterPoint = parts[1],
shouldRoundUp = afterPoint[dp] >= 5,
finalNumber;
afterPoint = afterPoint.slice(0, dp);
if (!shouldRoundUp) {
finalNumber = beforePoint + '.' + afterPoint;
} else if (/^9+$/.test(afterPoint)) {
// If we need to round up a number like 1.9999, increment the integer
// before the decimal point and discard the fractional part.
finalNumber = Number(beforePoint)+1;
} else {
// Starting from the last digit, increment digits until we find one
// that is not 9, then stop
var i = dp-1;
while (true) {
if (afterPoint[i] == '9') {
afterPoint = afterPoint.substr(0, i) +
'0' +
afterPoint.substr(i+1);
i--;
} else {
afterPoint = afterPoint.substr(0, i) +
(Number(afterPoint[i]) + 1) +
afterPoint.substr(i+1);
break;
}
}
finalNumber = beforePoint + '.' + afterPoint;
}
// Remove trailing zeroes from fractional part before returning
return finalNumber.replace(/0+$/, '')
}
උදාහරණ භාවිතය:
> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'
ඉහත කාර්යය බොහෝ විට පරිශීලකයින් තමන් ඇතුළු කර ඇති සංඛ්යා වැරදි ලෙස වට කර ඇති බව වළක්වා ගැනීමට භාවිතා කිරීමට අවශ්ය දේ විය හැකිය .
(විකල්පයක් ලෙස, ඔබට වෙනස් ආකාරයකින් ක්රියාත්මක කිරීම හා සමානව ක්රියා කරන රවුන්ඩ් 10 පුස්තකාලය උත්සාහ කළ හැකිය .)
නමුත් ඔබට දෙවන වර්ගයේ අංකයක් තිබේ නම් - අඛණ්ඩ පරිමාණයකින් ගත් අගයක්, අඩු දශම ස්ථාන සහිත දළ වශයෙන් දශම නිරූපණයන් වැඩි සංඛ්යාවක් ඇති අයට වඩා නිවැරදි යැයි සිතීමට හේතුවක් නොමැති නම් කුමක් කළ යුතුද? එවැනි අවස්ථාවකදී, අපට නූල් නිරූපණයට ගරු කිරීමට අවශ්ය නැත , මන්ද එම නිරූපණය (පිරිවිතරයේ විස්තර කර ඇති පරිදි) දැනටමත් වටකුරු ය; "0.014999999 ... 0.015 දක්වා වට 375 ක් වන අතර එය 0.02 දක්වා වටය, එබැවින් 0.014999999 ... 375 වට 0.02 දක්වා" යැයි පැවසීමේ වැරැද්ද කිරීමට අපට අවශ්ය නැත.
මෙහිදී අපට සරලවම ගොඩනංවන toFixed
ක්රමය භාවිතා කළ හැකිය . Number()
ආපසු එවූ toFixed
නූල් ඇමතීමෙන් අපට ලැබෙන්නේ ශුන්ය අගයක් නොමැති අංකයක් ලැබෙන බවයි (මෙම පිළිතුරෙහි මුලින් සාකච්ඡා කළ අංකයක නූල් නිරූපණය ජාවාස්ක්රිප්ට් ගණනය කරන ආකාරයට ස්තූතියි).
/**
* Takes a float and rounds it to at most dp decimal places. For example
*
* roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
*
* returns 1.234
*
* Note that since this treats the value passed to it as a floating point
* number, it will have counterintuitive results in some cases. For instance,
*
* roundFloatNumberWithoutTrailingZeroes(0.015, 2)
*
* gives 0.01 where 0.02 might be expected. For an explanation of why, see
* http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
* roundStringNumberWithoutTrailingZeroes function there instead.
*
* @param {number} num
* @param {number} dp
* @return {number}
*/
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
var numToFixedDp = Number(num).toFixed(dp);
return Number(numToFixedDp);
}