gcc: printf и long double приводят к неправильному выводу.[C - преобразование типов не работает] - PullRequest
4 голосов
/ 21 августа 2011

Я довольно новичок в C. Я пытаюсь написать функции для Vector, но должно быть что-то не так.
Вот код:

/* Defines maths for particles. */

#include <math.h>
#include <stdio.h>

/* The vector struct. */
typedef struct {
    long double x, y, z;
} Vector;

Vector Vector_InitDoubleXYZ(double x, double y, double z) {
    Vector v;
    v.x = (long double) x;
    v.y = (long double) y;
    v.z = (long double) z;
    return v;
}

Vector Vector_InitDoubleAll(double all) {
    Vector v;
    v.x = v.y = v.z = (long double) all;
    return v;
}


Vector Vector_InitLongDXYZ(long double x, long double y, long double z) {
    Vector v;
    v.x = x;
    v.y = y;
    v.z = z;
    return v;
}

Vector Vector_InitLongDAll(long double all) {
    Vector v;
    v.x = v.y = v.z = all;
    return v;
}

Vector Vector_AddVector(Vector *v1, Vector *v2) {
    Vector v3;
    v3.x = v1->x + v2->x;
    v3.y = v1->y + v2->y;
    v3.z = v1->z + v2->z;
    return v3;
}

Vector Vector_AddDouble(Vector *v1, double other) {
    Vector v2;
    v2.x = v1->x + other;
    v2.y = v1->y + other;
    v2.z = v1->z + other;
    return v2;
}

Vector Vector_AddLongD(Vector *v1, long double other) {
    Vector v2;
    v2.x = v1->x + other;
    v2.y = v1->y + other;
    v2.z = v1->z + other;
    return v2;
}

void Vector_Print(Vector *v) {
    printf("X: %Lf, Y: %Lf, Z: %Lf\n", v->x, v->y, v->z); //Before edit: used %ld
}

double Vector_Length(Vector *v) {
    return pow(pow(v->x, 2) + pow(v->y, 2) + pow(v->z, 2), 0.5);
}



int main() {
    Vector v = Vector_InitDoubleXYZ(2.0, 1.0, 7.0); //Before edit: (2.0d, 1.0d, 7.0d);

    Vector_Print(&v);
}

Я использую gcc для компиляции,Выполнение vector.exe в командной строке дает мне следующий вывод:

X: 0, Y: -2147483648, Z: 9650176

, и я не понимаю, почему это

Я ценю любые подсказки (даже о моем стиле кодирования или о том, что можно было бы улучшить в коде).
Спасибо,

Обновление : Использование компилятора MSVC работает просто отлично, похоже, это проблема gcc.Вы знаете, почему это происходит?

Ответы [ 5 ]

13 голосов
/ 21 августа 2011

Проблема (после устранения различных проблем при использовании целочисленных спецификаторов для форматирования с плавающей запятой) заключается в том, что вы смешиваете типы GCC со средой выполнения MSVC, которая их не понимает.

Во-первых, MinGWкомпилятор GCC, но он использует среду выполнения MSVC для большей части поддержки времени выполнения.Для семейства функций printf() это означает, что будут работать только те спецификаторы формата, которые поддерживает msvcrt.dll, и будут работать только те типы, которые msvcrt.dll.Но GCC ничего не знает об этом, поэтому он будет передавать свои собственные типы и, конечно, спецификаторы формата - это то, что вы передаете в строке формата (хотя GCC может выдавать предупреждения, которые на самом деле не относятся к msvcrt.dll ситуация).См. Странное поведение "unsigned long long int" для некоторых примеров, основанных на 64-битных целых числах (я думаю, что более новые версии msvcrt.dll, возможно, исправили некоторые или все проблемы 64-битных int).

Другая часть этой проблемы, с которой вы сталкиваетесь, заключается в том, что long double в GCC отличается от long double в MSVC.GCC использует 96-битный или 128-битный тип для long double для целей x86 или x64 (см. http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html). Однако MSVC использует 64-битный тип - в основном long double точно такой же, как doubleдля msvcrt.dll (http://msdn.microsoft.com/en-us/library/9cx8xs15.aspx):

Предыдущие 16-разрядные версии Microsoft C / C ++ и Microsoft Visual C ++ поддерживали тип данных long double, 80-bit precision. Однако в программировании Win32длинный двойной тип данных отображается на двойной тип данных с 64-битной точностью. Библиотека времени выполнения Microsoft предоставляет длинные двойные версии математических функций только для обратной совместимости. Прототипы длинных двойных функций идентичны прототипам по своим двойным аналогам,за исключением того, что тип данных long double заменяет тип данных double. Длинные двойные версии этих функций не должны использоваться в новом коде.

Так что это сводится к тому, что GCC / MinGW *Тип 1027 * просто не будет совместим с отформатированным вводом / выводом в msvcrt.dll. Либо переключитесь на использование double с MinGW, либо, если вам нужноe long double вам придется привести значения к (double) для форматированного ввода-вывода или придумать свои собственные процедуры форматирования.

Другой вариант может заключаться в использовании компилятора GCC в Cygwin, который яДумаю, мы не будем полагаться на msvcrt.dll для форматирования ввода / вывода (за счет использования среды Cygwin).

7 голосов
/ 21 августа 2011

Ваша строка формата не соответствует вашему типу. %ld - это спецификатор формата для long int, а не long double. Используйте %Lg.

2 голосов
/ 21 августа 2011

Должно быть:

void Vector_Print(Vector *v) {
    printf("X: %Lf, Y: %Lf, Z: %Lf\n", v->x, v->y, v->z);
}

С f (или g или e или G или E) для типа с плавающей запятой и с заглавными буквами L для long double.

Это стандартный C и C ++ спецификатор для long double. Использование строчных букв l может работать в некоторых реализациях, но зачем делать вашу программу менее переносимой, чем это необходимо.


Примечание. В семействе функций scanf спецификаторы немного отличаются. % f (и другие) означает float,% lf означает double, а% Lf означает long double.

В printf нет спецификатора для float. Вам нужно привести переменные с плавающей запятой к удвоению.


Примечание 2. Очевидно, что у mingw есть некоторые несовместимости, вызванные использованием среды выполнения MSVC, которые вызывают проблемы с% Lf. Это странно, потому что обычно MSVC допускает как% lf, так и% Lf.

Mingw имеет альтернативную реализацию stdio. Вы можете включить его, #defining __USE_MINGW_ANSI_STDIO в 1 ( см. ). Я не могу обещать, что это поможет. Я не очень хорошо знаю Mingw.

Обновление: ответ Майкла Барра объясняет, почему преобразование% Lf завершается неудачно при использовании среды выполнения MSVC с MinGW. Если вам все еще нужно использовать MinGW, попробуйте перейти на собственную реализацию. Использует настоящий длинный двойной тип.

0 голосов
/ 21 августа 2011

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

0 голосов
/ 21 августа 2011

Попробуйте что-то вроде этого:

Vector v;
Vector_InitDoubleXYZ(&v, 2.0d, 1.0d, 7.0d);

, где эта функция определена как:

void Vector_InitDoubleXYZ(Vector *v, double x, double y, double z) {
long double t;
t = x;
v->x = t;
t=y;
v->y = t;
t=z;
v->z = t;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...