大家好,我是伟伟。周末在网上看到一个关于微信钱包取现手续费的问题。说实话,就这个问题,我个人认为,放眼整个金融界,乃至整个弱智,乃至整个东半球,这是一个非常具有爆炸性的问题。一时之间,我居然看得眼花缭乱:一看,漏洞百出。不过仔细分析一下,原来TMD刀枪不入?!哦,这个问题你不能仔细琢磨,一琢磨脑袋就痛。要知道,我是维权者,所以我首先要验证一波微信提现的手续费是不是这么多。于是我发起了提现,发现确实要收至少一毛钱的手续费:另外我发现安卓手机不能在这个页面截图,所以我只能通过拍照的方式得到这张图,所以看起来好像有点别扭,还请多多关照小弟。那么第一个问题来了:我取一毛钱,它收一毛钱。请问最后拿到了多少?0.1(yuan)-0.1(yuan)=0(yuan),出金手续费全部扣掉,所以手头没钱。这样对吗?逻辑上是有道理的,但如果微信真的敢这么做,那岂不是很可爱(愚蠢)和可爱(被迫)?我举个例子:假设我向你借了100块钱,然后我通过微信还了你的银行卡(假设微信支持这个功能,类似于跨行转账),这笔转账对应的手续费是1元。如果从转账金额中扣除,收到的钱是99元。你觉得这有道理吗?如果觉得合理,请借我1w,我有急事,十分钟还你9900元。什么,你问我还有100块钱吗?别问了,手续费而已,你去微信吧。所以,正常的逻辑是从你的余额中扣除。比如我有141.02元的余额,我取出一毛钱,那么我的余额就变成了140.82元:这里,我也隐藏了一个逻辑。比如你只取一分钱,如果你的微信余额大于0.1元,也会被收取0.1元的手续费。这句话让我想哭。由于是从余额中扣除的,当我的余额只有10美分时,我又提取了10美分。这时候余额还不够扣,怎么办?我也赶紧试了一下:140.82(元)-140.72(元)=0.1(元)于是,我先给Max转了140.72(元):这时候我的微信钱包里只剩下0.1元了:这个那个时候有一次,当我再次提现0.1元时,发现微信居然告诉我:这次提现是免费的!!!由此可以看出,当微信中的余额不足以抵扣手续费时,本次提现将免费。这个是免费的,把它圈起来,后面测试你。实验完了,要回钱才说:啊!粗心!这样一来,我的文章成本就会很高。我被这个实验愣了一下,居然主动交了私房钱?但是我还有一个实验场景没做呢?即当我微信钱包里的钱大于0.1元时,点击“全部提现”会怎样?于是我成功的拿回了一分钱,实验的正当理由:就这样,我的余额变成了0.11元:所以,当我点击“全部提现”的时候,虽然我已经预料到了这一幕,但我还是沉默了,深深地沉默了。我出0.11元,手续费0.1元,账户0.01元。也就是说,当你取完所有现金,且取款金额大于手续费,即0.1元时,微信的逻辑是从你取的钱中扣除手续费。也就是我之前举的转账的例子,真的有可能转账的时候钱少了。麻绳只断在精处,霉运只属于穷人!现在,我得出一个结论,不能再输入密码了,再输入密码,又输一毛钱!现在实验结束了,我们有了结论。那么,接下来,我们来看第一个问题,让整个智障酒吧都“炸”的问题:通过上面的实验,我们知道,这道题中的一句话“IfIOnlytake0.1,thenitcharges0.1”作为手续费,没有问题。后半句:“意思是我一分钱都没拿到。”这句话值得商榷,因为有实验证明,一开始我确实从我的银行卡上收到了0.1元。但是,你要注意,我说的是“但是”。比如我有1块钱,每次取0.1元,手续费是0.1元。5次后,我微信里的1块钱变成银行卡0.5元,微信收取的手续费0.5元。那么,如果……我是说,如果我把银行卡里的0.5元充值回微信,继续重复上面的动作,事情会不会开始变得有趣起来呢?那么,面试编程题来了,请听题:众所周知,从微信钱包中提取任何金额,至少收取0.1元手续费,余额不足0.1元除外。假设小明现在有100块钱,他应该怎么做才能把这100块钱尽可能的变成手续费,白白送给微信呢?请给我一段Java代码。输入参数为微信钱包中的余额,log会打印出相应的操作过程。当你得到问题时,不要惊慌并分析它。首先100元,如果我每次只取0.1元,收0.1元手续费,那么我操作500次后,我还有50元。500次,刚好是微信余额,100元乘以10,单位换算成角度,再除以2。然后存50元,提现250次。250次,刚好是微信余额,50元乘以10,换算成角,再除以2。然后把25元存回去,再取125次。125次,刚好是微信余额,25元乘以10,换算成角,再除以2。然后把12.5块存回去取出62次,...然后存6.2挡回去取出31次,……循环往复,对吧?也就是说,我每操作一次,我的微信余额就会减少0.2元。结合前面的例子,不难推导出我每一轮的操作次数等于微信余额乘以10,单位换算成角度后,再除以2。这个程序是不难,直接上手:publicstaticvoidsbBehavior(doubleamount){//应该有个amount小于0的边界条件吧,节省篇幅,就不写了。if(amount<=0.1){//微信里的钱不够扣手续费,运行结束System.out.println("马化腾:你只有:"+amount+"元,谢谢,老铁~”);返回;}//金额增加了十倍,所以更容易计算doubletotalJiao=amount*10;//每一轮的操作次数等于微信余额除以2intcount=(int)(totalJiao/2);//每一轮结束后,总费用doublefee=count*0.1;//每一轮结束后,银行卡中剩余的钱双倍余数=count*0.1;System.out.println("微信原钱包金额="+金额+"元,操作次数="+计数+"次,手续费="+手续费+"元,余额="+余数+"元");//银行卡里剩余的钱充回微信,开始下一轮sbBehavior(remainder);}嗯,按照前面的思路,我写了这个程序,大家先看看这个程序有什么问题.我会明确的告诉你,这个程序肯定有问题,你才能搞清楚是什么问题。来,我问你:谁教你用浮点数计算金额的?回去等通知。当我们的输入参数为100时,运行上面的程序后,你会发现结果是这样的:所以,记住,只要涉及到金额的计算,就必须使用BigDecimal。并且在职场上也给大家一个救命之心:使用BigDecimal时,小数点后保留多少位,具体的四舍五入规则,一定要由提出需求的一方白纸黑字写好,而不是你自己写的认为是理所当然的。两位小数,四舍五入即可。如果后面有问题,你就把需求弹出来,不会很被动。回到我们的程序,那么我们要把程序修改成这样:publicstaticvoidsbBehavior(BigDecimalamount){if(amount.compareTo(newBigDecimal(0.1))<=0){//微信里的钱不够了手续费已扣除,操作结束。System.out.println("马化腾:你只有:"+amount+"元,谢谢老铁~");返回;}//金额增加十倍,元角,易计算BigDecimaljiao=amount.multiply(BigDecimal.TEN);//每轮操作次数等于微信余额除以2BigDecimalcount=jiao.divide(newBigDecimal(2),0,RoundingMode.DOWN);//每轮结束后总手续费BigDecimalfee=count.multiply(newBigDecimal(0.1));//每一轮结束后,银行卡中剩余的钱BigDecimalremainder=count.multiply(newBigDecimal(0.1));System.out.println("微信钱包原始金额="+金额+"元,操作次数="+计数+"次,手续费="+手续费+"元,余额="+余数+"元");//将银行卡中剩余的钱充值回微信,开始下一轮sbBehavior(remainder);}上面程序中,我将所有涉及运算的地方都改成了BigDecimal。但是这个程序还是有问题的。来吧,你继续琢磨,有什么问题?来来来,我问你:谁教你在使用BigDecimal参与计算时使用newBigDecimal(0.1)构造方法?如果你使用这种方法,idea会提醒你:老铁,听听大哥的建议,还是使用String类型的构造函数比较好。至于上面的程序,我帮你运行一下,你就会发现问题所在。浮点数还有一个问题:所以,程序只好改用BigDecimal的String类型的构造函数,其他什么都不改:嗯,这个问题解决了。你继续说,这个程序有什么问题?如果没看到,我给你看一下输出:这个输出中,手续费是6.2元,剩余金额是6.2元,加起来是12.4元。但是我的“微信钱包原金额”是12.5元?另一分钱去哪儿了?所以开始分析题的时候,我给大家定了一套:100块钱,500次运算后,还有50块钱。50元,250次运算后,还剩25元。25元,150次操作后,还有12.5元。。。如果你不按照自己的思路看文章,那你可能会默认一个操作后,手续费和银行卡余额都会增加0.1元。也就是说,手续费和银行卡的金额是和操作次数有关的,所以这段代码写成:手续费确实是每次操作后扣0.1元,确实和次数正相关操作。但剩余的钱应该是本轮的总剩余金额减去本轮的总手续费。也就是说,你需要将这行代码修改成这样:当你运行这个程序时,你会发现输出是正常的,而且每一轮的金额加起来是相等的:没有错现在。所以,小心点,我现在要开始转型了。我想把题目变成:请给我一段Java代码,入参是微信钱包里的余额,出参是需要操作的总次数。在问题中,将操作总数添加为输出参数。我已经知道每一轮的操作次数。计算总次数不是很容易吗?分分钟拿出代码:运行结果:我们可以看到是999次。是的,不要质疑这个结果。当你有100元的时候,你只需要操作999次,你就会把你的99.9元交给微信。嘿,朋友,请注意,当我将金额更改为50元时,总次数为499次;当我把金额改成9.9元时,总次数就变成了98次:so,请注意,我要“so”。那么,如果我只求操作总数而不求输出过程,那么代码应该是什么样的呢?你是不是把金额增加十倍,变成一毛钱,然后减去你剩下的一毛钱,就是总的操作次数:subtract(BigDecimal.ONE).intValue();}这不是游戏结束了吗?你把前面的内容都忘了,仔细想想,是不是真的?假设你有100元,不管你怎么操作,微信每次只会收取0.1元的手续费,你最多只剩下0.1元。那你至少要操作999次,这个小弯才能转过来吗?好吧,我们开始吧?我再问你一个问题,假设我只有0.19元,我想把钱给微信,最多一个操作,然后给0.1元,对吧?但是,如果你用完我上面给你的代码,输出是0:是的,这段代码还是有问题。我明确告诉你,这个码只适用于100.9元到0.19元之间的号码。至于为什么,你自己想想吧。但是我不建议大家去想,因为这东西从一开始就出了问题。现在,请忘记之前的所有代码,因为之前的代码都是错误的,我一路误导你,让你跟着我的思路走。其实你回想一下,当初我为什么要假设你微信里只有100元?因为100元对应的手续费,不管你取一毛钱还是取100元,100*0.001=0.1元就是0.1元。那我就开始告诉你,你每次提现到银行卡,手续费是0.1元,0.1元,blahblahblah~但是,你有没有想过,或者当你看到哪一部分的时候,突然悟道:如果我有1000元呢?如果我有1000块钱,那么第一次全部取走,手续费是1块钱,而不是0.1块钱?那么,现在回过头来看这行代码,是不是特别好笑:怎么可以先计算次数,再根据次数计算金额呢?为了尽快把所有的钱都打到微信上,每次都要尽量多收手续费。众所周知,手续费率是固定的,所以取款金额越高,手续费就越高,对吧?所以,正确的操作应该是每次都把微信钱包里的钱全部拿出来,也就是根据微信钱包里的钱来计算手续费和剩余的钱。换了个核心思想后,代码就变成这样了:我也给大家贴一段代码,可以粘贴使用:publicstaticintsbBehavior(BigDecimalamount,inttotalTimes){if(amount.compareTo(newBigDecimal("0.1"))<=0){//微信里的钱不够扣手续费,操作结束System.out.println("马化腾:你只有:"+amount+"元,谢谢,老铁~”);返回总次数;}//根据微信钱包里的钱计算手续费BigDecimalfee=amount.multiply(newBigDecimal("0.001")).setScale(2,BigDecimal.ROUND_UP);//手续费不足If(fee.compareTo(newBigDecimal("0.1"))<=0){fee=newBigDecimal("0.1");}//取到银行卡的钱BigDecimalremainder=amount.subtract(fee);总次数++;System.out.println("原现金="+金额+"元,运算后="+totalTimes+"次,手续费="+手续费+"元,剩余="+余数+"元");//把银行卡里剩余的钱充回微信,开始下一轮returnsbBehavior(remainder,totalTimes);}这样,当我们有1000元的时候,每次做“”取出所有现金的动作"只需要操作3257次:9999次效率提升300%+。舒服的!而且这是一个通用的逻辑,就算你给它100元,它也能给你算999次:给它0.19元,它能给你算1次:没毛病,但是,我不知道看到这里,是不是产生了一个疑问:为什么要给微信尽可能多的钱?那我再给大家换一个角度:我们应该如何操作才能避免支付微信费用?这样一想,你的脑洞是不是就开阔了?最后,如果这篇文章中有让你发笑的时刻,那么要求免费“点赞”也不过分吧?
