Как конвертировать последние 3 цифры номера в 0 - PullRequest
2 голосов
/ 14 ноября 2008

Как преобразовать последние 3 цифры номера в 0

пример с 3444678 по 3444000

Я могу сделать как

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

Но деление и умножение могут быть дорогостоящими ...

Любое другое решение ????

Ответы [ 6 ]

11 голосов
/ 14 ноября 2008

Вы можете попробовать

n - (n % 1000)

но оператор модуля может быть таким же дорогим, как и деление. В любом случае, это звучит очень похоже на микрооптимизацию. Это действительно ваше узкое место?

5 голосов
/ 14 ноября 2008

Смена трюк, затем:

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

Это быстрее? Сомнительно.

Но вот забавный факт: gcc для этого не использует деление / модуль:

n -= n % 1000;

Он умножается на какое-то сумасшедшее число (274877907) и делает другие вещи, которые предположительно быстрее.

Мораль этой истории: чем более очевидна цель вашего кода для компилятора, тем больше вероятность того, что компилятор оптимизирует его так, как вы никогда бы не подумали. Если код легче понять людям, это еще один бонус.

1 голос
/ 14 ноября 2008

Если вы работаете с восьмеричным, вы можете просто:

x &= ~0777;

Если вы работаете с гексом, вы можете просто:

x &= ~0xfff;

Но для десятичной дроби вы, вероятно, должны делать это так, как предлагает Джесси Бедер:

x -= (x % 1000);

Большинство систем имеют быстрое целочисленное деление; если вы работаете в системе, которая этого не делает, вы можете использовать алгоритм double dabble для быстрого преобразования в bcd. Посмотрите на раздел о делении на десять.

В зависимости от того, что еще вы пытаетесь сделать, может быть легче усечь при преобразовании числа в печатный (строковый) формат, как предложил Avitus.

1 голос
/ 14 ноября 2008

Кстати, кстати (вы уже получили хороший вклад).

Работа с битами никогда не работает с десятичными числами. Проблема в том, что значения битов вообще не отображаются в десятичную. С BCD это прекрасно работает, но никто никогда не использует это (возможно, BigDecimal делает ???, хотя я сомневаюсь в этом).

В любом случае, один трюк с базовым 10, который вы можете использовать, умножается на коэффициенты 10, но это никогда не стоит, если вы не программируете сборку на каком-то процессоре 1970-х годов; но только потому, что это загрязняет мои банки памяти, я выложу это для вашего развлечения:

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, но я не собираюсь пытаться выяснить это - уже поздно - Если я правильно помню, это как-то связано с изучением сдвинутых битов и добавление значений обратно на основе их значения.

0 голосов
/ 14 ноября 2008

Если вы хотите округлить с произвольной точностью, а вашему методу округления не хватает контроля точности, это должно сработать:

По крайней мере, в Perl мод должен быть быстрее

Вот почему: Образец (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 * 3 - это REDICULOUS число операций с плавающей запятой. Я не мог использовать 10**8 с плавающей точкой, потому что для честного теста мне пришлось использовать одну и ту же последовательность с плавающей точкой для всех тестов, и это число с плавающей точкой исчерпало мой 4G памяти

Хорошо, побочная тема: /

(Реализация Perl использует только Int, который усекается вместо обычного округления)

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 )
0 голосов
/ 14 ноября 2008
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, затем добавляете нули. Не может быть проще!

У меня возникло желание добавить int temp = num , а затем использовать его, но я подумал, что это будет слишком, даже для этого кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...