一滴"血液"引发的"血案",是谁减掉了那一滴"血液"
本帖最后由 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
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)
看起来就是JS的一系列设置造成了现在的情况呢 原来JavaScript是小偷偷走了那一滴血液:lol 这就是大佬的求知欲和探知精神吗:funk:震撼到我了! 支持科普{:5_117:}咱最开始发现的是换100个灵魂可以“优惠”1血液,最后经提醒得知低至3灵魂就可以实现了(笑)
但是为什么偏偏是3,6,7而不是其他的数字呢?感觉还是有点不明白,看看楼主给的参考文献吧:D 好好好,楼主也忒劲了,很强! 之前换东西时候就发现这个偷税漏税的方法了……捂脸当时是真的缺血液 我靠,虽然看不懂,但是看着就很专业:lol 哇哇哇,牛啊,这科普看完之后还是很惊讶的,有被震惊到,增加了一点点js的小知识,不错哎,这下知道那一滴血为什么没了(但是,3k多血液,这得攒多久) 新知识get,现在新勋章的升级消耗也越来越高了,不知道下次有没有机会试一下一次换三只灵魂{:6_167:} 现在补脑剂也容易触发内鬼扣血,明明就百分之2. 技术帝还是强,佩服佩服 阿巴阿巴(´×ω×`)虽然看不懂,但大受震撼.jpg
嘛~懂了,下次一口气换三个灵魂好了,能省就省 我看不懂,但是我大受震撼.jpg
为了偷一滴血的税也真是煞费苦心了{:6_188:} 这都能发现,程序大佬恐怖如斯 因为泥潭有批量单点献祭的脚本,发现之后都直接用脚本了,后来也只换咒术没换过别的了
羡慕楼主的求真精神,虽然我看得似懂非懂{:4_114:}{:4_114:}
当然这3只灵魂的一点血跟我也没有任何关系就对了{:4_90:} 泥潭数学家出现了{:4_114:} 大学时老师课上讲过这个
不同位数,浮点数的运算会导致意想不到的错误
当时并不明白
后来屡屡在知乎以及其他地方看到匪夷所思的错误只能感叹一句
真的厉害 好厉害哦
奇怪的姿势又增加了呢
那我就把那滴消失的血液献给楼主吧