2017年3月10日金曜日

小数点以下の桁を含む数値を一時的に整数化する

例えば「(-20.41) - (-43.3)」をJScriptで実行すると、答は「22.89」になる筈ですが実際には「22.889999999999997」という結果になります。そういう仕様ですが、もちろんこれでは困ります。
計算中のみ一時的に10とか100などの数字をかけて整数にして、最後に桁を元通りにすれば上記の問題を回避できます。

そのための関数を作成しました。

以下ソース
<job>
<script>
LEN='length'
forIn = function(obj,fun){var name,res; for(name in obj){if(res=fun(name, obj[name])){return res}}}
switch(2){
case 0:
// 以下では「66.9+0.01」の結果が「66.89000000000001」になってしまう。
整数化=function(){
var reg=/\.(\d+)/, get桁数=function(num){return (num+'').match(reg) ? RegExp.$1.length : 0}
var obj0, arr0, arr0L, obj1, forIn用=function(name,v){ arr0[arr0L++]=get桁数(v) }, max, x, 反映=function(name,v){ obj1[name] = v * x }
var check=function(name,v){(v+'').match(reg) && WScript.Echo([v,x].join('\n'))}
return function(obj, fun){
arr0 = []
arr0L= 0
obj1 = {}
forIn(obj0=obj, forIn用)
max=Math.max.apply(null, arr0), x=(1+Array(max+1).join(0))-0
forIn(obj0, 反映)
forIn(obj1, check)
return fun(obj1) / x
}
}()
break
case 1:
// 以下では「1.234e-16」のような値に対応できない。
整数化=function(){
var reg=/\.(\d+)/, arr, arrL, obj0
var getObj=function(num){
var str=num+'', obj={元:num, str:str.replace(reg,'$1'), 桁数:arr[arrL++]=RegExp.$1.length}
return obj
}
return function(obj, func){
arr = []
arrL = 0
obj0 = {}
forIn(obj,function(name, v){ obj0[name] = getObj(v) })
var max=Math.max.apply(null, arr), obj1={}
forIn(obj0,function(name, v){
obj1[name] = (v.str + Array(max-v.桁数+1).join(0)) - 0
// 万が一整数化に失敗した場合はどのような値で失敗したのか通知する。
if(0<(''+obj1[name]).indexOf('.')){ WScript.Echo('整数化失敗\n'+obj1[name]+'\nstr:'+v.str) }
})
return func(obj1) / (1+Array(max+1).join(0))
}
}()
break
case 2:
整数化=function(){
var regDot=/\.(\d+)/, regE=/\.(\d+)e-(\d+)/, arr, arrL, obj0
var getObj=function(num){
var str=num+'', 桁数=0
while(1){
if(str.match(regE )){ 桁数=RegExp.$2-(-3) ; str=str.replace(regE ,'$1'); break }
if(str.match(regDot)){ 桁数=RegExp.$1.length; str=str.replace(regDot,'$1'); break }
break
}
return {元:num, str:str, 桁数:arr[arrL++]=桁数}
}
return function(obj, func){
arr = []
arrL = 0
obj0 = {}
forIn(obj,function(name, v){ obj0[name] = getObj(v) })
var max=Math.max.apply(null, arr), obj1={}
forIn(obj0,function(name, v){
obj1[name] = (v.str + Array(max-v.桁数+1).join(0)) - 0
// 言語で扱える数字のサイズを超えてしまった場合は通知する。
if(!isFinite(obj1[name])){ alert('整数化失敗\nisFinite=false\n'+obj[name]+'\nstr:'+str) }
// 万が一整数化に失敗した場合はどのような値で失敗したのか通知する。
if(0<(''+obj1[name]).indexOf('.') && !(''+obj1[name]).match(/e\+/)){ WScript.Echo('整数化失敗\n'+obj1[name]+'\nstr:'+v.str) }
})
return func(obj1) / (1+Array(max+1).join(0))
}
}()
break
}
a = -20.41
b = -43.3
c = a - b
d = 整数化({a:a, b:b}, function(obj){return obj.a - obj.b})
e = 66.9
f = 0.01
g = 整数化({e:e, f:f}, function(obj){return obj.e - obj.f})
h = 90071992547409.92
i = 1.234e-16
j = 整数化([h,i], function(arr){return arr[0] - arr[1]})
// 22.889999999999997
// 22.89
WScript.Echo([c,d,g,j].join('\n'))
</script>
</job>
view raw 整数化.wsf hosted with ❤ by GitHub


実行結果


0 件のコメント:

コメントを投稿