Листинг uint32_t для uint64_t приводит к разному значению?

Использование Visual Studio 2015 С++, 14.0.25431.01 Обновление 3. У меня неожиданное поведение в моем коде. Скомпилируйте и запустите с 64-битным, Release:

#include <iostream>
#include <stdint.h>

int main(int, char**) {
    for (uint32_t i = 1; i < 3; ++i) {
        uint32_t a = i * 0xfbd1e995;
        uint64_t b = a;

        std::cout << a << " 32bit" << std::endl;
        std::cout << b << " 64bit" << std::endl;
    }
}

Я ожидаю, что a и b имеют одинаковое значение, но когда я запустил это, я получаю этот вывод:

4224838037 32bit
4224838037 64bit
4154708778 32bit
8449676074 64bit

Похоже, что компилятор заменяет 32-битное умножение на 64-битное умножение. Разрешено ли это делать, или это ошибка компилятора? И g++, и clang дают мне числа, которые я ожидаю.

EDIT: я обновляю свой код с более простой версией, которая имеет ту же проблему. Кроме того, Я только что отправил отчет об ошибке.

Ответы

Ответ 1

Похоже, это исправление устраняет проблему, по крайней мере, для VS 2015:

https://support.microsoft.com/en-us/help/3207317/visual-c-optimizer-fixes-for-visual-studio-2015-update-3

Но похоже, что VS 2008, 2010, 2013 все еще затронуты этой ошибкой.

Источники:

Ответ 2

Я мог бы воспроизвести это на VS2010, и непосредственной причиной этого является следующее:

add ebx, 5BD1E995h  ; this is x
add rdi, 5BD1E995h  ; this is a 64bit version of x

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

Также интересно, что он даже не сохраняет бросок, ошибочно комментируя его. Правильное значение находится прямо в rbx.