【问题标题】:How to convert last 3 digits of number into 0如何将数字的最后3位转换为0
【发布时间】:2008-11-14 01:06:48
【问题描述】:

如何将数字的后三位转换为0

示例 3444678 到 3444000

我可以做到

(int)(3444678/1000) * 1000= 3444000

但是除法和乘法可能代价高昂......

还有什么办法吗????

【问题讨论】:

  • 如果数字是整数,那么除法和乘法应该不会太昂贵。确保它们是整数,并确保除法是整数除法。我只能猜测您使用的是什么语言,因此无法提供适当的语法建议。
  • 对不起,我只是注意到一些乐于助人的人删除了您最初包含在问题中的 c++ 标签。我真的不明白人们为什么要进行这种编辑。
  • 是的,不知道为什么人们编辑标签..????我认为这不应该被允许编辑其他人的内容......
  • 我知道这个问题是要求一个数学解决方案。然而,一个简单的字符串格式化解决方案难道不是最简单、最明显的吗?

标签: optimization math


【解决方案1】:

你可以试试

n - (n % 1000)

但是模运算符可能与除法一样昂贵。无论如何,这听起来很像微优化。这真的是你的瓶颈吗?

【讨论】:

  • 不,这不是任何瓶颈..我只是想知道可能有一些位操作技巧来解决这个问题,比如移位运算符或其他东西..
  • 这是一个不错的选择,但恐怕它太聪明了。原来的方式更清楚你在做什么。另外,正如您所说,加/除与减/模的成本相同。
  • 我实际上发现它比原版更具可读性(因为这取决于整数的截断,这对我来说一直是不明显的),但这是一个品味问题。无论如何,原来的实际上是乘法/除法,可能比减法/模慢一点。
  • 乘/除肯定比减/模慢。 Mod通常使用与除法完全相同的电路(通常是完全相同的指令)来实现,并且只使用结果的另一半。倍数当然比减法复杂。
  • 一个 div 需要 18 纳秒?那是敲诈勒索!一个光子可能会从我的电脑机箱里、窗外,以及大部分时间在前花园里!
【解决方案2】:

换档技巧,然后:

n >>= 3;
n -= n % (5 * 5 * 5);
n <<= 3;

更快吗?值得怀疑。

但这里有一个有趣的事实:gcc 不为此使用除法/模数:

n -= n % 1000;

它乘以某个疯狂的数字 (274877907) 并执行一些其他可能更快的操作。

这个故事的寓意:您的代码对编译器的目的越明显,编译器就越有可能以您从未想过的方式对其进行优化。如果代码更容易被人类理解,那就是另一个好处了。

【讨论】:

  • 该总结/道德措辞完美。我想我会把它作为我的标语。
【解决方案3】:

顺便说一句(你已经在这里得到了很好的输入)。

位操作永远不能用于十进制数。问题是位的值根本不映射到十进制。使用 BCD 效果很好,但没有人使用它(也许 BigDecimal 会?我对此表示怀疑)。

无论如何,您可以使用的一个以 10 为基数的技巧是乘以 10 倍数,但除非您在 1970 年代的 CPU 上编写汇编代码,否则这是不值得的;但是因为它污染了我的记忆库,所以我会发布它以供您娱乐:

int mult10(int val) {
    int tmp_2val = val << 1; // double val
    int tmp_8val = val << 3; // 8x val
    return( tmp_2val + tmp_8val ); // 2x + 8x = 10x
}

但是数学协处理器可以比这快得多,只是永远不要优化!您甚至将执行速度考虑在内这一事实是一个问题,您的“优化”通常会减慢系统速度而不是加快系统速度。

我相信您可以使用类似的方法除以 10,但我不会试图弄清楚 - 已经晚了 - 如果我没记错的话,它与检查移出的位有关根据值重新添加值。

【讨论】:

  • 有时在某些极端情况下,微优化会带来好处。尤其是在一些事情上。我看到了一个 3n+1 的挑战,其中通过执行 >> 1 而不是 / 2 来增加巨大的加速
  • 另外,如果您使用的是二进制编码的十进制 (BCD),那么只有这样您才能在 base10 中进行按位数学运算:)
  • 我在回答中提到了 BCD。此外,具有数学协处理器的系统应该在一个周期内完成几乎所有操作——关键是,它可能会稍微快一点,也可能不会,它从来不重要,如果是这样,测试是唯一的方法知道。
  • @Kent Fredric - 如果 >> 1 在特定 CPU 上比 / 2 快,并且编译器没有用移位替换恒定的二次幂除法,那么我想知道编译器编写者整天实际上做什么。他们应该考虑那些东西,所以我不必;-)
【解决方案4】:

如果你正在处理八进制,你可以简单地:

x &= ~0777;

如果你正在使用十六进制,你可以简单地:

x &= ~0xfff;

但是对于十进制,您可能应该按照 Jesse Beder 建议的方式进行操作:

x -= (x % 1000);

大多数系统都有快速整数除法;如果您正在使用的系统没有,您可以使用double dabble 算法快速转换为 bcd。查看关于除以十的部分。

根据您尝试执行的其他操作,将数字转换为 Avitus 建议的可打印(字符串)格式时可能更容易截断。

【讨论】:

    【解决方案5】:
    int takeAway(int num)
    {
        for (int i = 1; i =< 3; i++)
        {
            num = num/10;
        }
    
        return num;
    }
    
    int addZeroes(int num)
    {
        for (int i = 1; i =< 3; i++)
        {
            num = num*10;
        }
    
        return num;
    }
    

    然后你只需调用 takeAway 然后 addZeroes。再简单不过了!

    我很想添加一个 int temp = num 然后使用它,但我认为这太多了,即使对于这段代码也是如此。

    【讨论】:

    • 这涉及循环,比做 (num/1000)*1000 成本更高
    • 讽刺在互联网上不起作用。下次尝试使用笑脸。 :)
    • 自然而然。鉴于每个人都说这很好,我觉得有必要提供一个“替代方案”。不过,它确实有效!
    • 理查德,你是在暗示这不是玩笑吗?我现在真的很困惑:-(
    • 我投了这个票,因为它可能是这里最好的解决方案。可重复使用且意图明确。当程序员担心简单除法/乘法的性能时,这不是一个好兆头!这意味着他不明白!
    【解决方案6】:

    如果您想四舍五入到任意精度并且您的舍入方法缺乏精度控制,这应该可以:

    至少在 Perl 中,mod 应该仍然更快

    原因如下: ( 10 ** 7 ) * 3 个随机浮点数的样本产生了这个基准:

    Dividing
    Time taken was  7 wallclock secs ( 6.83 usr  0.00 sys +  0.00 cusr  0.00 csys =  6.83 CPU) seconds
    
    Multiplying
    Time taken was  7 wallclock secs ( 6.67 usr  0.00 sys +  0.00 cusr  0.00 csys =  6.67 CPU) seconds
    
    Modding
    Time taken was  8 wallclock secs ( 7.87 usr  0.00 sys +  0.00 cusr  0.00 csys =  7.87 CPU) seconds
    
    Multiply + Dividing
    Time taken was 10 wallclock secs (10.18 usr  0.01 sys +  0.00 cusr  0.00 csys = 10.19 CPU) seconds
    

    请注意,10 ** 7 * 3REDICULOUS 的浮点数。我不能使用 10**8 浮点数,因为为了公平测试,我必须对所有测试使用相同的浮点数序列,而且许多浮点数耗尽了我的 4G 内存

    好的,有点偏题了:/

    (仅使用 Int 的 Perl 实现,通常截断而不是四舍五入)

    use strict;
    use warnings;
    use version;
    our $VERSION = qv('0.1');
    
    sub xround { 
        my ( $number, $precision ) = @_ ;
        if ( $precision eq 0 )
        {
            return int( $number + .5 ); 
        }
        my $scale = 10 ** abs( $precision ) ;
    
        $number = $number / $scale if $precision > 0; 
        $number = $number * $scale if $precision < 0; 
    
        $number = int( $number + .5 );
    
        $number = $number * $scale if $precision > 0; 
        $number = $number / $scale if $precision < 0; 
        return $number;
    }
    
    my $fmt = "%s : %s  ( %s )\n";
    my $n = 55555.55555;
    for my $i ( -4 .. 4 )
    {
    
        printf $fmt, $n, xround($n, $i), $i; 
    }
    

    .

    55555.55555 : 55555.5556  ( -4 )
    55555.55555 : 55555.556  ( -3 )
    55555.55555 : 55555.56  ( -2 )
    55555.55555 : 55555.6  ( -1 )
    55555.55555 : 55556  ( 0 )
    55555.55555 : 55560  ( 1 )
    55555.55555 : 55600  ( 2 )
    55555.55555 : 56000  ( 3 )
    55555.55555 : 60000  ( 4 )
    

    使用模数法

    这和上面一样工作,(除了没有四舍五入)使用整数的模数方法,但仍然适用于四舍五入到浮点精度(小数点右边的精度有点慢。

    use strict;
    use warnings;
    use version;
    our $VERSION = qv('0.1');
    
    sub xround {
        my ( $number, $precision ) = @_;
        my $ino = int( $number );
        if ( $precision eq 0 ) {
            return $ino; 
        }
        my $power = 10**$precision;
    
        if ( $precision > 0 ) {
            return int( $number - ( $number % $power ) );
        }
        return $ino + int( ( $number - $ino ) / $power ) * $power;
    }
    
    
    my $fmt = "%s : %s  ( %s )\n";
    my $n = 55555.55555;
    for my $i ( -4 .. 4 )
    {
        printf $fmt, $n, xround($n, $i), $i; 
    }
    

    .

    55555.55555 : 55555.5555  ( -4 )
    55555.55555 : 55555.555  ( -3 )
    55555.55555 : 55555.55  ( -2 )
    55555.55555 : 55555.5  ( -1 )
    55555.55555 : 55555  ( 0 )
    55555.55555 : 55550  ( 1 )
    55555.55555 : 55500  ( 2 )
    55555.55555 : 55000  ( 3 )
    55555.55555 : 50000  ( 4 )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-25
      • 1970-01-01
      • 2022-08-16
      • 2013-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-24
      相关资源
      最近更新 更多