你好呀,我是歪歪。

周末的时分,我在网上看到一个关于微信钱包提现时,手续费收取的一个问题。

从“提现手续费”,我想到一个面试题,你来试一试?

说真的,就这个问题吧,我个人觉得,放眼整个金融界,乃至于整个弱智吧,甚至于整个东半球,这都是一个相当迸裂的问题啊。

一时间,我竟然模糊了起来:一眼看去,漏洞百出。可是仔细剖析之后,竟然 TMD 无懈可击?!

哎呀,这个问题,你就不能细揣摩,一揣摩,脑瓜仁就疼。

从“提现手续费”,我想到一个面试题,你来试一试?

你知道的,我是一个行动派,所以我必定得先验证一波微信提现的手续费是否是这么多。

所以我建议提现了,发现的确是有至少一角钱的手续费:

从“提现手续费”,我想到一个面试题,你来试一试?

另外,我发现安卓的手机,在这个页面中还无法截图,所以我就只要经过拍照的办法搞到这个图片了,所以看起来有点别扭,你多谅解一下小老弟。

那么第一个问题就来了:我提现一角,它手续费一角。请问我最终到手是多少钱呢?

从“提现手续费”,我想到一个面试题,你来试一试?

0.1(元)-0.1(元)=0(元),提现的钱悉数扣了手续费,所以没有钱到手。

对吗?

逻辑上是合理的,可是假如微信真的敢这样做的话,不就显得很可(傻)爱(逼)吗?

给你举个比方:假定,我找你借了 100 元钱,然后我经过微信还给你的银行卡(假定微信支持这个功能,类似于跨行转账),此刻这笔转账对应的手续费是 1 元。假如从转账的金额里边扣除,你收到的钱是 99 元。

你觉得这合理吗?

假如你觉得合理的话,那么请借给我 1w 吧,我应个急,十分钟后就还你 9900 元。

什么,你问我还有 100 元呢?

别问,问便是手续费,你找微信要去。

所以,正常的逻辑是从你的余额里边扣除。比方在我余额还有 141.02 元的时分,我提取了一毛钱,那么我的余额变成了 140.82 元:

从“提现手续费”,我想到一个面试题,你来试一试?

这里,我还隐藏了一个逻辑。

比方你只提现一分钱的时分,假如你的微信余额大于 0.1 元,那么也是要收取 0.1 元的手续费的。

这句话,听起来就想要流泪。

从“提现手续费”,我想到一个面试题,你来试一试?

既然是从余额中扣除,那么当我余额只要一毛钱的时分,我再次提现一毛钱,这个时分余额不够扣了,会呈现什么情况呢?

我也赶紧试了试:

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 元的时分,微信的逻辑是从你提取的钱里边扣除手续费。

也便是说我前面举得转账的比方,是真的有或许呈现转出去,钱少了的情况。

从“提现手续费”,我想到一个面试题,你来试一试?

麻绳专挑细处断,厄运专找薄命人啊!

从“提现手续费”,我想到一个面试题,你来试一试?

现在,我现已得到定论了,所以我不能再输入暗码了,再输入暗码,又得痛失一毛钱!

试验现在现已完毕了,定论咱们也现已有了。

那么,接下来,咱们再看看最开端的,那个让整个弱智吧,都为之“迸裂”的问题:

从“提现手续费”,我想到一个面试题,你来试一试?

经过上面的试验,咱们得知,这个问题中的这句话“假如我每次都只取 0.1,然后它手续费收 0.1”是没有任何问题的。

后半句:“就等于我一分钱都没有拿到”。

这句话是值得商榷的,由于经过试验证明,我最开端的时分,的确银行卡到账了 0.1 元。

可是,你要留意,我说“可是”了。

从“提现手续费”,我想到一个面试题,你来试一试?

比方,我 1 元钱,每次提取 0.1 元,手续费 0.1 元,这样 5 次之后我微信里边的 1 元钱就变成了银行卡 0.5 元和微信收取的手续费 0.5 元。

那么,假如…

我是说假如,我把我银行卡里边的 0.5 元再次充回到微信里边,持续重复上面的动作,工作是不是就开端变得风趣了?

从“提现手续费”,我想到一个面试题,你来试一试?

所以,面试编程题就来了,请听题:

已知,在微信钱包提现恣意金额,都会收取至少 0.1 元的手续费,可是当余额缺乏 0.1 元时在外。假定,小明现在有 100 元,他应该怎样操作,才能把这 100 元钱,尽或许的悉数变成手续费,白白送给微信?

请给我一段 Java 代码,入参是微信钱包里边的余额,日志打印出对应的操作进程。

拿到题,先不慌,剖析一波。

首要,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+"元了,谢谢老铁~");
return;
}
//金额扩展十倍,元转角,好核算
doubletotalJiao=amount*10;
//每一轮的操作次数,等于微信余额除以2
intcount=(int)(totalJiao/2);
//每一轮完毕之后,共计手续费
doublefee=count*0.1;
//每一轮完毕之后,银行卡里剩下的钱
doubleremainder=count*0.1;
System.out.println("微信钱包原金额="+amount+"元,操作次数="+count+"次,手续费="+fee+"元,剩下金额="+remainder+"元");
//把银行卡里剩下的钱充回微信,开端下一轮
sbBehavior(remainder);
}

好,按照前面的思路,我写出了这个程序,你就先看这个程序有什么问题。我就明着告知你,这个程序必定是有问题的,你就去揣摩,到底有哪些问题。

从“提现手续费”,我想到一个面试题,你来试一试?

来,我问你:谁教你金额核算用浮点型的?回去等通知吧。

当咱们的入参为 100 的时分,上面那个程序跑完之后,你会发现成果是这样的:

从“提现手续费”,我想到一个面试题,你来试一试?

所以,牢记在心,只需涉及到金额的核算,必定必定必定要用 BigDecimal。并且我还附送你一条职场保命心经:用到 BigDecimal 时,详细保留多少小数位,详细的四舍五入规矩,必定必定必定要让需求提出方白纸黑字的写在需求里边,而不是你自己想当然的以为,保留两位小数,选用四舍五入就行。后边出问题了,你啪的一下,便是把需求拿出来,你就不会很被动了。

回到咱们的程序中,所以咱们应该把程序修改成这样:

publicstaticvoidsbBehavior(BigDecimalamount){
if(amount.compareTo(newBigDecimal(0.1))<=0){
//微信里的钱不够了扣手续费了,操作完毕
System.out.println("麻花藤:你只剩下:"+amount+"元了,谢谢老铁~");
return;
}
//金额扩展十倍,元转角,好核算
BigDecimaljiao=amount.multiply(BigDecimal.TEN);
//每一轮的操作次数,等于微信余额除以2
BigDecimalcount=jiao.divide(newBigDecimal(2),0,RoundingMode.DOWN);
//每一轮完毕之后,共计手续费
BigDecimalfee=count.multiply(newBigDecimal(0.1));
//每一轮完毕之后,银行卡里剩下的钱
BigDecimalremainder=count.multiply(newBigDecimal(0.1));
System.out.println("微信钱包原金额="+amount+"元,操作次数="+count+"次,手续费="+fee+"元,剩下金额="+remainder+"元");
//把银行卡里剩下的钱充回微信,开端下一轮
sbBehavior(remainder);
}

在上面的程序中,我把参加运行的地方悉数都改成了 BigDecimal。可是这个程序仍是有问题。

来,你持续去揣摩,到底有哪些问题?

从“提现手续费”,我想到一个面试题,你来试一试?

来,我问你:谁教你用 BigDecimal 参加核算的时分,用 new BigDecimal(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 次:

从“提现手续费”,我想到一个面试题,你来试一试?

所以,请留意,我要“所以”了。

所以,假如我只需求操作的总次数,不要求输出进程,那么代码应该是怎样样的?

是不是把金额扩展十倍,变成角票,然后减去自己留下的一角钱,便是操作的总次数:

publicstaticintsbBehavior(BigDecimalamount){
returnamount.multiply(BigDecimal.TEN).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 元,巴拉巴拉巴拉~

可是,你有没有想过,或者是看到哪个部分的时分,才恍然大悟:假如我有 1000 元呢?

假如我有 1000 元,那么我第一次悉数提现的话,手续费便是 1 元啊,而不是 0.1 元啊?

所以,你现在回过头去看这行代码,是不是特别的搞笑:

从“提现手续费”,我想到一个面试题,你来试一试?

怎样会去先核算次数,再根据次数反算其金额呢?

为了赶快的把钱都给到微信,必定是每次尽量给到更多的手续费。已知手续费率是固定的,那么提现的金额越高,手续费越高对吧?

所以,正确的操作应该是每次把微信钱包里边的钱悉数都取出来,也便是根据微信钱包里边的钱,去核算手续费,核算剩下的钱。

转变了核心思路之后,代码就变成了这样:

从“提现手续费”,我想到一个面试题,你来试一试?

我也给你放一个粘曩昔就能用的代码:

publicstaticintsbBehavior(BigDecimalamount,inttotalTimes){
if(amount.compareTo(newBigDecimal("0.1"))<=0){
//微信里的钱不够了扣手续费了,操作完毕
System.out.println("麻花藤:你只剩下:"+amount+"元了,谢谢老铁~");
returntotalTimes;
}
//根据微信钱包里边的钱,去核算手续费
BigDecimalfee=amount.multiply(newBigDecimal("0.001")).setScale(2,BigDecimal.ROUND_UP);
//手续费缺乏0.1元,则补齐为0.1元
if(fee.compareTo(newBigDecimal("0.1"))<=0){
fee=newBigDecimal("0.1");
}
//提现到银行卡的钱
BigDecimalremainder=amount.subtract(fee);
totalTimes++;
System.out.println("原现金="+amount+"元,操作="+totalTimes+"次后,手续费="+fee+"元,还剩下="+remainder+"元");
//把银行卡里剩下的钱充回微信,开端下一轮
returnsbBehavior(remainder,totalTimes);
}

这样,当咱们有 1000 元的时分,每次做“悉数提现”的动作,只需求操作 3257 次:

从“提现手续费”,我想到一个面试题,你来试一试?

从“提现手续费”,我想到一个面试题,你来试一试?

假如咱们仍是用之前一毛钱一毛钱的提法,得搞 9999 次。

功率提升 300%+。

舒服了!

并且这个是通用的逻辑,你就算给它 100 元,它也能给你算出是 999 次:

从“提现手续费”,我想到一个面试题,你来试一试?

给它 0.19 元,它能给你算出是 1 次:

从“提现手续费”,我想到一个面试题,你来试一试?

没有任何缺点,可是,不知道你看到这里,是否产生了一个疑问:为什么咱们要把钱尽或许的给微信呢?

从“提现手续费”,我想到一个面试题,你来试一试?

那我给你换个视点:咱们应该怎样操作,才应该防止给微信手续费呢?

这样一想,是不是思路就打开了?

从“提现手续费”,我想到一个面试题,你来试一试?