本帖最后由 GMagels 于 2024-4-1 21:49 编辑
血液祭献换取属性值的时候采取的是向下取整,因此可以通过每次只换取一点属性来保证交的税最小化。前几天楼主发了一篇日志,计算了一下泥潭手动祭献的情况下怎么样选择属性值的数量来少交税。
在日志中,使用了Excel拉表,根据每个属性值的初始价格,以及税率值15%,计算了不同数量下每点属性值的平均价格,并在最后给出了这样子的结论:
总结来说,最划算的方式肯定还是每次只兑换1点,除此之外的第二选择旅程和知识是3点,咒术是2点。
如果只是要避免交最多的税的情况的话,只要注意不要兑换偶数数量的旅程和知识以及4的倍数个咒术就OK了。
灵魂的税是整数,不存在避税方式,所以请随意兑换XD
坛友是万能的,在发布日志当天,收到了这样的评论:
但是一次性兑换3个灵魂只要3449血液,超值折扣,赶快去兑换吧()
看到这条回复的楼主愣了一下,啊!?这不对吧,怎么会少一滴?1000点血液*15.00%的税率应当缴纳150滴血液税,向下取整也还是150滴血液啊,怎么辉石呢?
由于楼主在计算的时候只是使用Excel对数字进行计算,并没有真的去页面一个一个数字去试,秉承着“实践是检验真理的唯一标准”,马上就去祭献页面进行了测试.......
结果...真的是3449....是我血液不够换灵魂的错.....该不会....该不会换三个灵魂真的有特殊折扣吧.....
虽然可能永远不会血液换灵魂,但是这个东西就像是谜底还没有揭开的你画我猜,一直搞的人心痒痒的。
于是楼主决定去翻代码,来看看到底是不是折扣。很快便翻到了这句向下取整数量的计算语句:
- $('exchangedesamount').value = Math.floor( tocredit.getAttribute('ratio') / fromcredit.getAttribute('ratio') * $('exchangeamount').value * (1 + 0.15));
复制代码 仔细研究研究,
Math.floor() 是用于向下取整的函数,没什么问题。
tocredit.getAttribute('ratio') / fromcredit.getAttribute('ratio')
两个属性的比例值这一点也没问题,1000/1 = 1000。
$('exchangeamount').value * (1 + 0.15))
最后这个是输入的数量乘上1.15,看着也没有任何问题。
这就奇怪了,这个结果的计算过程就应该是1000*N*1.15啊,怎么会出现3449这个数字。
就在这一筹莫展之际,气急败坏的楼主使用了搜索引擎Bing,计算了3000*1.15,然后居然发现:
没错,搜索引擎居然也给出了3449的答案!(是真的,大家可以自己去试)至此,这场未定的悬案出现了一丝破解谜题的关键线索。
顺着这条线索楼主又进行了一番搜索,终于找到了减掉这一滴血液的小黑的身份,他就是.....
BGM:bilibili
⬆CODE区不支持内嵌视频或音乐,请打开这个网站来播放BGM,唉嘿!
没错小黑就是.......JavaScript!
JS:证据呢,证据!你凭什么说是我扣掉的那一滴血液。
确实,指证犯人需要确凿的证据,但是今天想要获取证据并不难,楼主悄悄写了几行JS代码并运行,结果如下:
没错!当乘数为3000,6000,7000,也就是灵魂数为3,6,7时,得出来的结果会比应该的结果少一丢丢,然后再通过向下取整发的方式,就能完美实现少扣一滴血液!
JS:那作案动机呢?都花3500血液换灵魂了,我为什么还要去减一滴血液?
经过楼主缜密的调查,JS里的数字是采用 IEEE 754 标准的 64 位双精度浮点数(百度百科链接)。
JS存储的数字一共由64个0或1组成,这其中,第0位表示"正负"(蓝色),第1-11位存储"指数"(绿色),第12-63位存储"有效数字"(红色)。
存储有效数字部分的位数是52位,而很不幸,浮点数(带小数的数)的二进制是无限循环的,比方说这次出场的1.15经过二进制转换是:
1.0010011001100110011001100110011001100110011001100110011......(无限循环)
在JS存储时,这个无限循环的数只被记了前52位,后面的都被抹掉了。
正是因为无限循环的数在存储时被抹掉后续的一部分,1.15在这一步中损失了它精度,造成了后续计算在与3000,6000,7000相乘时得出来的数字与本应得出来的结果差了1。
对此非常感兴趣的也友友可以去这篇知乎专栏了解详细的原因:JavaScript 浮点数运算的精度问题
终于,一切水落石出,案结。
第一次在CODE区发帖,如果有误欢迎指正!如果觉得有意思,也欢迎给个免费的追随。
有代码基础的友友,可以自己运行一下下面这段代码(会弹窗),亲自看一下JS实际运算的结果。
<script type="text/javascript">
window.alert("1000*1*(1+0.15) = "+ 1000*1*(1+0.15) +" \n" +
"1000*2*(1+0.15) = "+ 1000*2*(1+0.15) +" \n" +
"1000*3*(1+0.15) = "+ 1000*3*(1+0.15) +" \n" +
"1000*4*(1+0.15) = "+ 1000*4*(1+0.15) +" \n" +
"1000*5*(1+0.15) = "+ 1000*5*(1+0.15) +" \n" +
"1000*6*(1+0.15) = "+ 1000*6*(1+0.15) +" \n" +
"1000*7*(1+0.15) = "+ 1000*7*(1+0.15) +" \n" +
"1000*8*(1+0.15) = "+ 1000*8*(1+0.15) +" \n" +
"1000*9*(1+0.15) = "+ 1000*9*(1+0.15) +" \n" );
</script>
↑点击第一个按钮即可直接运行(再次提醒:会弹窗XD)
|