LINUX.ORG.RU

Странные значения в математической функции

 


0

3

Есть функция:

Point pshift(Point pt1, Point pt2, uint32_t value)
{
    float D = distance((Rect){pt1, pt2});
    if (D == 0) return pt1;
    Point result;
    result.x = pt1.x + (value * (pt2.x - pt1.x)) / D;
    result.y = pt1.y + (value * (pt2.y - pt1.y)) / D;
    return result;
}
Point - struct с двумя int32_t, Rect - два Point.

Приходят на вход значения:

p1 = (186, 92)
p2 = (362, 27)
value = 50
На выходе по иксу все ок, по игреку - ересь: 22892002.

Почему?

Царя можете не вносить, все и так знают, что все, включая K&R не знают си.

★★

Последнее исправление: cetjs2 (всего исправлений: 1)

Вангую, проблема спрятана где-то в uint32_t в аргументах + отрицательное значение pt2.y-pt1.y

Deleted
()
Ответ на: комментарий от Deleted

Вот черт, забыл, что все к u приведется. Спасибо.

sambist ★★
() автор топика

Результат вычитания перед умножением на value приводится к unsigned. Соответствие пункту стандарта сам найдешь.

alegz ★★★★
()
Ответ на: комментарий от alegz

А не подскажешь, почему так работает?

  int16_t a=27,b=362,c=2,d=2,r;
  uint64_t e = 3;
  r = c + (e * (a - b))/d;
Получается r=-501 и чтобы получить абракадабру надо явно кастануть (a-b) к unsigned.

naszar
()

Point - struct с двумя int32_t,

А там точно signed int?

andreyu ★★★★★
()

float D = distance((Rect){pt1, pt2});

Ох уж эти шутники... Жесть же! Зачем так писать? Да, новомодный С это позволяет. Но убого же! И совершенно неоднозначно. И без бэкпортов...

А целочисленная арифметика — она такая. С ней осторожно надо быть.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от const86

Ну, у меня и с флоатами как-то раз проблемы были (нужно было double использовать, т.к. на нескольких итерациях накапливалась огромная ошибка из-за низкой точности float).

Но у ТСа вообще непонятная какая-то смесь: и uint, и int, и float — и все в одном выражении!

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от sambist

и а и b - signed, их результат - signed

Ну да.. но e - unsigned.. собственно выражение как у ТС'a, но можно оптимизировать во время компиляции.. не понимаю, почему не работает:

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

Тут явно signed*unsigned=signed.. и это не прикол компилятора, но особенность языка.. какая?

naszar
()
Ответ на: комментарий от naszar

и это не прикол компилятора, но особенность языка.. какая?

такая. Компилятор C может считать например X=(A-B)/C как X=A/C-B/C. Для него это одно и тоже. Но если типы разные, то при переводе получается ерунда. Поможет

X=A-B;
X=X/C;
emulek
()
Ответ на: комментарий от emulek

такая. Компилятор C может считать например X=(A-B)/C как X=A/C-B/C. Для него это одно и тоже.

Пилять, иди стандарт почитай. Компилятору запрещено оптимизировать изменять вычисления, если изменённое вычисление может порождать другой результат.

anonymous
()
Ответ на: комментарий от emulek

дык я кусок стандарта там выше скопипастил... получается врет стандарт? Я ему уже и --std=c99 подсовывал, и несколько гццов попробовал и даже силанг.. на всех правильный результат. А у ТС когда ему совсем не нужно все работает. От чего зависит? Это каждый раз надо думать как гцц скобки раскроет?

naszar
()
Ответ на: комментарий от anonymous

Пилять, иди стандарт почитай. Компилятору запрещено оптимизировать изменять вычисления, если изменённое вычисление может порождать другой результат.

ты в школе математику учил? (A-B)/C=A/C-B/C. Формально это тождество, проблемы реализаций стандарт не волнуют.

emulek
()
Ответ на: комментарий от naszar

От чего зависит?

от реализации.

Это каждый раз надо думать как гцц скобки раскроет?

да, конечно. Тебе сложно лишнюю временную переменную сделать, в которой ты приводишь всё к float/double? Тогда страдай.

Компилятор ещё и не то умеет. Например 3/3=-1. Бред, да? А вот и не бред, т.к. компилятор знает, что x/3==x/0x55555555, а вот 0x55555555*3=0xFFFFFFFF, т.е. -1.

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

emulek
()
Ответ на: комментарий от emulek

Тебе сложно лишнюю временную переменную сделать

кстати эта лишняя переменная всё равно будет выкинута оптимизатором. Но смысл в том, что:

1. из исходников будет явно видно, что ты тут с типами химичишь

2. результат будет всегда одинаковый (хотя и не всегда желаемый).

emulek
()
    float D =
    if (D == 0) 

Никогда не используй строгое равенство при сравнении вещественного числа с целым. Бывают сюрпризы.

akk ★★★★★
()
Последнее исправление: akk (всего исправлений: 1)
Ответ на: комментарий от emulek

ты в школе математику учил?

Ты идиот и не лечишься. Арифметика двоичных целых чисел имеет мало отношения к алгебре. Там свои собственные правила.

(A-B)/C=A/C-B/C. Формально это тождество

В математике. При чем тут Си?

проблемы реализаций стандарт не волнуют.

Ты этот стандарт почитай, дурашка. Я его читал. А ты — нет. Будь мужиком, отвечай за свои слова.

Ей-богу, глядя на вас, идиотов, иногда просто хочется начать крыть всех х**ми, как это делает Царь. Хотя Царь тоже идиот.

Во-первых,

Выражение (A-B)/C потенциально содержит ажно два UB: при вычитании интов и при делении. Выражение A/C-B/C потенциально содержит три ДРУГИХ UB.

Объясняю на пальцах: в A-B может случиться переполнение int; результат выражения — undefined behaviour. С другой стороны, если мы СНАЧАЛА ПОДЕЛИМ, то при вычитании переполнение может и не случится. В итоге, результат не будет undefined.

Во-вторых,

Для алгебраических целых чисел твоё тождество просто не работает. (3 - 1) / 3 = 2 / 3 = 0, но 3 / 3 - 1 / 3 = 1 - 0 = 1. Так что иди обратно в школу учи алгебру, нуб.

Итого. Компилятор не может заменять одно выражение на другое, потому что они семантически не эквивалентны.

anonymous
()
Ответ на: комментарий от naszar

Получается r=-501 и чтобы получить абракадабру надо явно кастануть (a-b) к unsigned.

А покажи-ка компилябельный код целиком, глянем.

anonymous
()
Ответ на: комментарий от naszar

Не вижу разницу с кастами и без.

#include <stdio.h>
#include <stdint.h>

int main()
{
	int16_t a=27,b=362,c=2,d=2,r;
	uint64_t e = 3;

	r = c + (e * (a - b))/d;
	printf("%d\n",r);

	r = c + (e * (unsigned)(a - b))/d;
	printf("%d\n",r);

	r = c + (e * ((unsigned)a - (unsigned)b))/d;
	printf("%d\n",r);

	return 0;
}

Результат:

-501
-501
-501

Это на 32-разрядной платформе. Но если задуматься, то и на 64 бита должно быть то же самое. Щас в виртуалке проверю.

anonymous
()
Ответ на: комментарий от anonymous

на 64 бита должно быть то же самое

Да. То же самое.

anonymous
()
Ответ на: комментарий от anonymous

Ты идиот и не лечишься. Арифметика двоичных целых чисел имеет мало отношения к алгебре. Там свои собственные правила.

идиот это ты. Проблема не в том, что правила свои, проблема в том, что эти правила везде _разные_. Это в принципе не стандартизировать, вот и приходится делать так, как оно есть. (в противном случае пришлось-бы понаставить кучу костылей и подпорок для тех платформ, где правила «не стандартные», что-бы подогнать под результат).

В математике. При чем тут Си?

не при чём. Просто ты неправильно понимаешь стандарт, когда цитируешь фразу «не может дать другой результат». На самом деле, это «не может» как раз в математическом смысле. Если не веришь, смотри мой пример про 3/3=-1 выше: компилятор так делает, и его не волнует, что результат разный с оптимизацией и без. Если-бы компилятор просто поделил-бы, как сказано, ответ был-бы верным, однако компилятор соптимизировал, и получился другой ответ после оптимизации.

Выражение (A-B)/C потенциально содержит ажно два UB: при вычитании интов и при делении. Выражение A/C-B/C потенциально содержит три ДРУГИХ UB.

я знаю. И что теперь? Мне код перестать писать? Как ты, что-ли? (ты наверное и не написал ни строчки).

Объясняю на пальцах: в A-B может случиться переполнение int; результат выражения — undefined behaviour. С другой стороны, если мы СНАЧАЛА ПОДЕЛИМ, то при вычитании переполнение может и не случится. В итоге, результат не будет undefined.

да. Это C, детка.

Для алгебраических целых чисел твоё тождество просто не работает. (3 - 1) / 3 = 2 / 3 = 0, но 3 / 3 - 1 / 3 = 1 - 0 = 1. Так что иди обратно в школу учи алгебру, нуб.

ты в свою школу хоть поступил? Обрати внимания, ты В ОБОИХ случаях получил неправильный результат. И что ты этим пытаешься доказать?

Ладно, объясни дураку, как _мне_ правильно писать? А то я, дурак, сначала думаю головой, а потом вычисляю по частям. Получаю стабильный результат, который, увы не всегда верен. А вот если такие выражения одной строкой писать, то результат получается, увы, нестабильный, ты правильно подметил, на одних платформах получится 0, а на других — единица. И когда ты напишешь хоть немного кода не только для своего локалхоста, то ты с этим столкнёшься, я гарантирую это.

Итого. Компилятор не может заменять одно выражение на другое, потому что они семантически не эквивалентны.

объясни тупому, что значит «семантически» в данном контексте?

emulek
()
Ответ на: комментарий от anonymous
	int16_t a=27,b=362,c=2,d=2,r;
	uint64_t e = 3;

	r = c + (e * (a - b))/d;

а теперь скомпилируй gcc -O2, и посмотри код. Оно ничего считать НЕ БУДЕТ. Естественно, результат одинаков.

emulek
()
Ответ на: комментарий от emulek

Очень даже будет. Совершенно так же считает: и при -O2, и при -O3.

Другое дело — если ты printf'ы выкинешь. Но тогда все очевидно.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от emulek

объясни тупому, что значит «семантически» в данном контексте?

Объясняю тупому еще раз:

Семантика выражений Си стандартизирована соответствующим документом. Там чётко и одно значно сказано, что ДОЛЖНА делать или не делать conforming implementation.

Пока ты тут разводишь сопли про «правила везде разные», нормальные люди уже давно прочитали ДОКУМЕНТ.

я знаю. И что теперь?

Нихера ты не знаешь. Если бы знал, не писал бы чушь здесь. Ты уже забыл, о чем мы говорили, Так я напомню: с точки зрения компилятора, те два выражения имют разный смысл, и следовательно менять их местами он имеет право не больше, чем менять a + b на a * b. То есть, никакого права не имеет.

Мне код перестать писать?

Учитывая, что ты нихера не знаешь язык, на котором «пишешь», можно представить, какой в итоге получается говнокод. «Ничего больше не трогайте, после переставления этих строчек местами программа наконец-то посчитала правильно!»

Как ты, что-ли? (ты наверное и не написал ни строчки).

В отличие от тебя, дуболома, я на Си пишу реальный код, а не примерчики для запуска на локалхосте. Поэтому я обязан знать, что этот код реально означает в соответствии со стандартами языка.

компилятор так делает, и его не волнует, что результат разный с оптимизацией и без.

Повторяю в третий раз для тупого: тебе нужно открыть стандарт и ПРОЧИТАТЬ ЕГО. А читая, умудриться ПОНЯТЬ ПРОЧИТАННОЕ. Тогда твои проблемы с выдумыванием, что «компилятор волнует или не волнует», отпадут сами собой. Там чётко написано, что компилятор ДОЛЖЕН ДЕЛАТЬ, что НЕ ДОЛЖЕН ДЕЛАТЬ, а что ИМЕЕТ ПРАВО ДЕЛАТЬ ИЛИ НЕ ДЕЛАТЬ по своему усмотрению.

ты правильно подметил, на одних платформах получится 0, а на других — единица

Лолшто? В том абзаце я вообще не говорил про «платформы», я говорил про алгербраические целые числа. Для которых определена такая операция: целочисленное деление с остатком. Математика, ферштейн? Нет, ты в школе учился в принципе? Алгебру изучал? Или ты на дереве просидел до 20-ти лет, а потом тебя поймали белые люди и посадили писать код за еду?

anonymous
()
Ответ на: комментарий от Eddy_Em

у тебя выражение содержит только константы. Потому всё выражение вычисляется во время компиляции.

пруф:

#include <stdio.h>
#include <stdint.h>

main()
{
	int16_t a=27,b=362,c=2,d=2,r;
	uint64_t e = 3;

	r = c + (e * (a - b))/d;
	printf("%d\n",r);
}

$ gcc -Wall -O2 t.c
t.c:4:1: предупреждение: по умолчанию возвращаемый тип функции - «int» [-Wreturn-type]
 main()
 ^
t.c: В функции «main»:
t.c:11:1: предупреждение: control reaches end of non-void function [-Wreturn-type]
 }
 ^
$ gcc --version
gcc (GCC) 4.8.3
Copyright (C) 2013 Free Software Foundation, Inc.
Это свободно распространяемое программное обеспечение. Условия копирования
приведены в исходных текстах. Без гарантии каких-либо качеств, включая 
коммерческую ценность и применимость для каких-либо целей.

080482f0 <main>:
 80482f0:	55                   	push   ebp
 80482f1:	89 e5                	mov    ebp,esp
 80482f3:	83 e4 f0             	and    esp,0xfffffff0
 80482f6:	83 ec 10             	sub    esp,0x10
 80482f9:	c7 44 24 04 0b fe ff 	mov    DWORD PTR [esp+0x4],0xfffffe0b
 8048300:	ff 
 8048301:	c7 04 24 20 85 04 08 	mov    DWORD PTR [esp],0x8048520
 8048308:	e8 b3 ff ff ff       	call   80482c0 <printf@plt>
 804830d:	c9                   	leave  
 804830e:	c3                   	ret    

где тут вычисления?

emulek
()
Ответ на: комментарий от emulek

Оно ничего считать НЕ БУДЕТ.

выражение вычисляется во время компиляции.

Взаимоисключающие параграфы такие взаимоисключающие.

anonymous
()
Ответ на: комментарий от emulek

да,но суть то не меняется:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
	int16_t a, b, c=2, d=5,r;
	uint16_t e = 3;
	
	a = atoi(argv[1]);
	b = atoi(argv[2]);
	r = c + (e * (unsigned)(a - b))/d;
	printf("%d\n",r);
	r = c + (e * (a - b))/d;
	printf("%d\n",r);
}
./unsigned 27 362
12908
-199

naszar
()
Ответ на: комментарий от anonymous

emulek - марковский процесс первого порядка. Он забывает всё, что дальше одного ответа по цепочке. С ним бесполезно спорить, не трать силы. Внезапно упомянуть в разговоре дельфинов, а потом допытываться, причём тут дельфины и зачем ты их упомянул, для него — обычное поведение.

i-rinat ★★★★★
()
Ответ на: комментарий от anonymous

Лолшто? В том абзаце я вообще не говорил про «платформы», я говорил про алгербраические целые числа. Для которых определена такая операция: целочисленное деление с остатком.

ладно. Разговор окончен. У тебя в голове каша: откуда материализовалось «деление с остатком»? Где в твоих выкладках этот остаток? Нет его там.

На остальное отвечу кратко: сам ты мудак.

emulek
()
Ответ на: комментарий от anonymous

Взаимоисключающие параграфы такие взаимоисключающие.

осиль узнать, что такое «во время исполнения» и «во время компиляции».

i-rinat как всегда: от тебя нет ни слова по теме. А твои мысли про мою персону всем тут наверное очень интересны, да?

emulek
()
Ответ на: комментарий от emulek

ладно. Разговор окончен. У тебя в голове каша: откуда материализовалось «деление с остатком»? Где в твоих выкладках этот остаток? Нет его там.

Ты и правда тупой. Операция деления, определенная для всех целых чисел — это деление с остатком.

«Деление просто» определено НЕ ДЛЯ ВСЕХ пар делимого и делителя. 2 на 3 не делится в целых числах. А деление с остатком — для всех.

А остаток мы отбрасываем, да. «Поделить с остатком и отбросить остаток». Вижу что такая сложная операция в твой мозг не уместится.

anonymous
()
Ответ на: комментарий от anonymous

так uint16_t или uint64_t

Там по большому разницы нет. Меняется только неверная циферька, когда явно кастуеш. Для uint64_t вот такой выхлоп:

./unsigned 27 362
858993260
-199
Явно проявляется с подсказанной Eddy_Em d=5...

naszar
()
Ответ на: комментарий от emulek

осиль узнать, что такое «во время исполнения» и «во время компиляции».

А Ринат-то прав. Ты дольше 5 минут контекст разговора удержать не способен. Какая разница, вычислится выражение «во время исполнения» или «во время компиляции»? Язык гарантирует, что в обоих случаях оно вычислится одинаково. РЕЗУЛЬТАТ ОДИНАКОВЫЙ.

anonymous
()
Ответ на: комментарий от naszar

правильно: в первом случае разность uint16_t, а во втором случае разность int16_t. И это твоё unsigned размножается как рак на всё выражение, смотри сам:

 8048366:	0f bf d6             	movsx  edx,si
 8048369:	98                   	cwde   
 804836a:	29 c2                	sub    edx,eax
 804836c:	8d 1c 12             	lea    ebx,[edx+edx*1]
 804836f:	01 d3                	add    ebx,edx
 8048371:	ba cd cc cc cc       	mov    edx,0xcccccccd
 8048376:	89 d8                	mov    eax,ebx
 8048378:	f7 e2                	mul    edx
 804837a:	c1 ea 02             	shr    edx,0x2
 804837d:	83 c2 02             	add    edx,0x2
 8048380:	0f bf d2             	movsx  edx,dx
 8048383:	89 54 24 04          	mov    DWORD PTR [esp+0x4],edx
 8048387:	c7 04 24 d0 85 04 08 	mov    DWORD PTR [esp],0x80485d0
 804838e:	e8 4d ff ff ff       	call   80482e0 <printf@plt>
 8048393:	ba 67 66 66 66       	mov    edx,0x66666667
 8048398:	89 d8                	mov    eax,ebx
 804839a:	f7 ea                	imul   edx
 804839c:	d1 fa                	sar    edx,1
 804839e:	c1 fb 1f             	sar    ebx,0x1f
 80483a1:	29 da                	sub    edx,ebx
 80483a3:	83 c2 02             	add    edx,0x2
 80483a6:	0f bf d2             	movsx  edx,dx
 80483a9:	89 54 24 04          	mov    DWORD PTR [esp+0x4],edx
 80483ad:	c7 04 24 d0 85 04 08 	mov    DWORD PTR [esp],0x80485d0
 80483b4:	e8 27 ff ff ff       	call   80482e0 <printf@plt>

emulek
()
Ответ на: комментарий от emulek

на лоре впринципе можно увидеть фразу «да, я ничтожество, пойду почитаю стандарт и не буду больше позориться»? или это фантастика?

anonymous
()
Ответ на: комментарий от naszar

Смотри:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#define typename(x) _Generic((x),\
  int16_t : "int16_t",\
  int32_t : "int32_t",\
  int64_t : "int64_t",\
 uint16_t : "uint16_t",\
 uint32_t : "uint32_t",\
 uint64_t : "uint64_t"\
)

int main(int argc, char **argv)
{
	int16_t a, b, c=2, d=5,r;
	uint16_t e = 3;
	
	printf("%s\n", typename(e * (unsigned)(a - b)));
	printf("%s\n", typename(e * (a - b)));
}

Результат:

$ gcc --std=c11 3.c && ./a.out
uint32_t
int32_t

Почему? Потому что «типы, ранг которых меньше int, возводятся в int, если int вмещает их значения; иначе — в unsigned int».

В выражении e * (a - b) все переменные перед вычислением апаются до int (т.к. они все в int поместятся). На 32-битной платформе int == int32_t, что мы и видим.

Но если мы кастуем операнд в unsigned, результат другой. uint16_t умножается на unsigned. Результат выражения будет unsigned, т.е. uint32_t.

Затем мы это значение будем делить на d. Битово значения одинаковые, но тип операнда разный. А знаковое и беззнаковое деление даёт физически разные результаты. Поэтому вот.

anonymous
()
Ответ на: комментарий от anonymous

Ты и правда тупой. Операция деления, определенная для всех целых чисел — это деление с остатком. «Деление просто» определено НЕ ДЛЯ ВСЕХ пар делимого и делителя. 2 на 3 не делится в целых числах. А деление с остатком — для всех. А остаток мы отбрасываем, да. «Поделить с остатком и отбросить остаток». Вижу что такая сложная операция в твой мозг не уместится.

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

А с чего им быть одинаковыми и правильными, если ты остатки выбросил? А вот ты не выбрасывай, и всё будет идеально. Я гарантирую это.

Пойми, проблема в том, что операция деления, определённая в стандарте, она изначально неточная. Если у тебя деление, то всё, БУДЕТ ошибка. Компилятора не волнуют твои проблемы, погрешность по определению НЕ ОПРЕДЕЛЕНА, и потому, если компилятор переделывает выражение, и при этом ошибка другая — ничего страшного.

Читай стандарт про точки следования — только так можно заставить компилятор делать всё по порядку. И никак иначе. Одно выражение == одна точка следования, и что там в ней случится — хз. Компилятор гарантирует лишь то, что результат преобразований верен в в случае, если операции и операнды считаются безошибочно.

emulek
()
Ответ на: комментарий от anonymous

на лоре впринципе можно увидеть фразу «да, я ничтожество, пойду почитаю стандарт и не буду больше позориться»? или это фантастика?

фантастика. Мы здесь уже читали стандарт со ссылками и цитатами. На эту тему тоже читали. Пошукай, если модераторы не удалили, то найдёшь в архиве.

А я спать, надоело. Ты далеко не первый.

emulek
()
Ответ на: комментарий от anonymous

Затем мы это значение будем делить на d. Битово значения одинаковые, но тип операнда разный. А знаковое и беззнаковое деление даёт физически разные результаты. Поэтому вот.

по сути ты прав, но в реальности деления там нет. А есть вот что:

 8048393:	ba 67 66 66 66       	mov    edx,0x66666667
 8048398:	89 d8                	mov    eax,ebx
 804839a:	f7 ea                	imul   edx

т.е. компилятор поменял деление на 5, на умножение на 0x66666667. Хватит уже константы пихать, компилятор их любит.

emulek
()
Ответ на: комментарий от anonymous

на лоре в принципе можно увидеть фразу

Конечно можно. Самоуничижения там правда нет.

i-rinat ★★★★★
()
Ответ на: комментарий от naszar

Для uint64_t

Теперь с этим разберёмся. Если тип e взять uint64_t, то тип выражений (e * (unsigned)(a - b)) и (e * (a - b)) будет одинаковый — uint64_t. Этот тип не ниже int-а рангом, поэтому int propagation для него не выполняется.

Но вот физически результат будет разный:
0x00000002fffffc13 == (e * (unsigned)(a - b))
0xfffffffffffffc13 == (e * (a - b))

Почему?

Выражение (a - b) имеет тип int. Если мы кастуем в (unsigned), значение становится (unsigned) без физического изменения битов. Затем для выполнения умножения значение расширяется нулями до 64-бит.

Иначе происходит, если мы не делаем каста вручну. Сначала int раширяется до int64_t, то есть не «расширение нулём», «расширением битом знака». А затем результат интерпретируется как беззнаковый.

Ну вроде разобрались. Действительно, при d == 2, разница скрадывается, а при d == 5 наглядно видна.

anonymous
()
Ответ на: комментарий от emulek

Пойми, проблема в том, что операция деления, определённая в стандарте, она изначально неточная.

ПРОЧИТАЙ СТАНДАРТ, ЖЫВОТНАЭ.

Компилятора не волнуют твои проблемы, погрешность по определению НЕ ОПРЕДЕЛЕНА

Компилятор действительно не волнуют твои проблемы. Например, твоя неспособность ЧИТАТЬ.

anonymous
()
Ответ на: комментарий от anonymous

А знаковое и беззнаковое деление даёт физически разные результаты. Поэтому вот.

Тут дело не в делении, а в касте во флоат. Умножению/делению пофиг на знаки и даёт оно абсолютно одинаковый результат.

Бомжам же надо просто везде юзать инты, чтобы не перенапрягать свои нерабочие полторы извилины.

Здесь знак нужен только при касте во флоат, ибо именно вот тут будет абсолютно разный результат. Последний бит перейдёт либо во знак, либо в значение.

carb_blog10
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.