2016-02-28 7 views
0

Я искал документы, но я не могу найти ничего, чтобы объяснить это.Почему переполнение приводит к 0?

У меня есть программа D:

import std.stdio; 
void main() { 
    writeln(int.max); 
    int a = 2; 
    int b = 180; 
    writeln(a^^b); 
} 

Он пишет:

2147483647 
0 

Я переполнена int, но вместо того, чтобы мусор или упаковку, я получаю 0.

Если я использую real или double, очевидно, что результат будет правильным.

Я написал программу, чтобы экспериментировать с этим в C (не то, что C является D, а D компилируется в машинный код и C портативный ассемблер, поэтому они должны быть сопоставимы):

#include <stdio.h> 
#include <math.h> 
#include <limits.h> 
int main(void) { 
    double i = pow(2, 128); 
    int j = (int) i; 
    printf("int max:   %d\n", INT_MAX); 
    printf("print dbl as int: %d\n", i); 
    printf("cast dbl -> int: %d\n", j); 
    printf("double:   %f\n", i); 
    return 0; 
} 

Это дает:

int max:   2147483647 
print dbl as int: -1254064128 
cast dbl -> int: -2147483648 
double:   340282366920938463463374607431768211456.000000 

Вторая и третья линии линия редко будет то же самое в два раза, так как я считаю, что это неопределенное поведение, которое является точкой.

Я знаю, что D хочет быть лучшим C, и способ сделать это - устранить неопределенное поведение.

Но если D является языком системного программирования (он даже имеет встроенный asm), почему D отказывается от переполнения?

ответ

4

Это DID обернуть на переполнение. Вы только что попробовали семейство дел, которые обернулись до нуля. Попробуйте 3^^180. Я получил -949019631. Просто потому, что число выглядит красиво на экране, это не значит, что это не мусор!

Рассмотрите, что 2 ^^ n == 1 < < n. Что происходит, когда вы переставляете один бит снова и снова? В конце концов, все биты справа становятся нулевыми! Затем, когда вы усекаете это, чтобы соответствовать 64-битовому значению, вы остаетесь со всеми равными нулю.


Но позвольте мне вдаваться в детали в любом случае. Во-первых, критический анализ вашего C:

// snip. note that i is type double 
    printf("print dbl as int: %d\n", i); 

Эта линия является неправильным на двух уровнях: она проходит 64-битный двойной, где Printf ожидает 32 битный INT, и это переосмысливать литье эти биты в целое, которое полностью отличается от преобразования в int.

Если вы хотите сделать это в D, вы должны явно переинтерпретировать биты, используя объединение или преобразование через промежуточный указатель. Вы даже можете срезать другие 32 бита, если хотите!

Следующая строка, которая использует правильное явное преобразование, написана правильно, но все еще неопределенное поведение, поскольку приведение двойного к int, когда оно слишком велико, чтобы соответствовать, не является ни C, ни D (или базовым оборудованием) обещает.


И обратно Д. ^^ оператор D просто переписывает выражение в std.math.pow(a, b).std.math.pow имеет различные реализации для разных типов. Поскольку оба аргумента являются неотъемлемыми здесь, он вообще не выполняет вычисления с плавающей запятой - он имеет чистую реализацию int/long, которая работает так же, как умножение.

Итак, ваше сравнение C не совсем верно, потому что в C вы использовали double и пытались преобразовать, тогда как в D он никогда не касался с плавающей запятой вообще. Целочисленное умножение определено для работы через дополнение и усечение двух, и это именно то, что произошло здесь. Он переполнился, оставив все нули в оставшихся битах.

+0

Просто примечание: точка двух строк C, которые являются неправильными, целенаправленно неопределенна/неверна, чтобы продемонстрировать результат, которого я ожидал. Но теперь я понимаю, так что спасибо. – cat

+1

«он переинтерпретирует литье этих битов в int», в C это неопределенное поведение *. Результат (если есть) может не иметь сходства с входом, например. на общих 64-битных системах значения с плавающей запятой передаются в printf с использованием разных регистров, чем для целочисленных значений. –

+0

Да, конечно, поэтому это так случайно! –

 Смежные вопросы

  • Нет связанных вопросов^_^