LINUX.ORG.RU

Почему C++ не может без потери данных сдвинуть запятую во float типе данных?

 ,


2

2

Привет всем!

Столкнулся с проблемой, простейшее умножение числа 0.56 на 10.0 не даёт точного результата. C++ просто не в состоянии перенести знак справа налево когда я хочу перенести разряд. Но при этом, 0.56 * 100.0 даёт точный ответ, точное число 56.0! Lol! ))))

Многие ответят скорее всего, что - «округли, да и всё!». Нет, округление не подходит, так как задача выполняется не в традиционных языках программирования, а в нодовой системе шейдеров Blender где ноды математики полагаются полностью на логику C++ в отношении математики и я не могу ничего с этим поделать кроме того, что доступно из математики в Блендер.

Да, в нодах есть операции округления, но мне это не подходит, потому что мне нужно получать из большого целого числа отдельные части разрядов, т.е. из числа 12345 получать отдельно число 1,2,3,4 и 5. При этом у меня нет никаких переменных, циклов и т.д.как в традиционных языках программирования. Есть только нодовый поток. Я научился это делать умножением и делением, получать отдельные разряды в нодовом потоке, но столкнулся со странной математикой в C++ на которые эти ноды опираются (полагаются).

Почему C++ не может просто сдвинуть запятую справа налево при умножении на 10, а при умножении на 100 может? Это баг какой-то или фича?

В других языках, которых я немного знаю, Java и Python (да, я понимаю, что это интерпретируемые языки) такого нет, результат всегда ожидаемый: 0.56 * 10.0 = 5.6 - P.S. Как оказалось - нет, см. комметарии.

https://godbolt.org/z/ErnbfePhf



Последнее исправление: dva20 (всего исправлений: 8)
Ответ на: комментарий от anonymous

А если чуть серьёзнее, то численные методы надо осиливать

https://www.ozon.ru/product/chislennye-metody-5291042/?sh=O5n71AAAAA

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

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

Не все рождаются в «резиновых».

я из глубинки урала))). за комп на информатике сел первый раз только в техникуме, в 1993 году. это был БК0010.

почему не у всех равные возможности доступа к обучению?

не актуально последние лет 10 с момента, когда инет стал «коммунальной услугой», а материала для образовани - вагонище.

в 2022 подобные заявления прям совсем дич )

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

А если чуть серьёзнее, то численные методы надо осиливать

Да тут всё проще, нужно просто знать тип данных float как он себя ведёт. Вы не встретите подобной «ошибки» на некоторых других типах данных с плавающей запятой.

dva20
() автор топика
Ответ на: комментарий от ergo

я из глубинки урала)))

Да какая это глубинка? ))) Вот где я родился и учился, вот там действительно глубинка )) Оймякон Полюс Холода. В -60 одеваешься и шлёпаешь в школу. Никаких ПК в той школе не видели.

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

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

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

Нет, тут важно ещё понимать как правильно считать. Флоаты это верхушка айсберга.

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

не актуально последние лет 10 с момента, когда инет стал «коммунальной услугой», а материала для образовани - вагонище.

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

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

Так что так вот…

dva20
() автор топика
Ответ на: комментарий от olelookoe

Да, так можно было бы сделать. Я знаю как деление по модулю работает, но его не рассматривал, так как там немного больше сложностей в потоке, например, как развернуть порядок, мне нужно ещё учитывать Capacity. Мне нужно получать 0,0,1,2,3,4,5, а не 5,4,3,2,1,0,0. Я не знаю как математикой развернуть обратно, чтобы Modulo выдавал другой порядок.

Хотя наверное можно, но это усложняет вычисления, их становится много. Например,

10000 = power(10, len(12345)) / 10 - у меня есть ещё «length», это как микропрограмма или пользовательский метод который считает длину математикой.

12345 Mod 10000 = 2345

12345 - 2345 = 10000/10000 = 1 (вывели это число на экран)

далее повторяем уменьшая разрядность для Mod делением на 10, но не забываем, что у нас тут везде float, плавает зараза хрен пойми в какую сторону.

2345 Mod 1000 = 345

2345 - 345 = 2000/1000 = 2 (вывели это число на экран)

345 Mod 100 = 45

345 - 45 = 300/100 = 3 (вывели это число на экран)

и так далее.

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

dva20
() автор топика
Последнее исправление: dva20 (всего исправлений: 9)
Ответ на: комментарий от dva20

Да, это было как бы понятно и до этого вопроса, вопрос в другом был, почему перенос запятой (умножение на 10.0 введенных с клавы) не может быть без потери правой части исходного числа. Ну пусть бы оно там плавало в большую сторону, и при умножении 0.56 * 10.0 выдавало бы 5.600000000001, но в реальности плавает в меньшую сторону и результат может быть 5.599999999998.

Потому что мантисса сдвигается, хвост теряется (или округляется, зависит от разного). Вот ещё несколько вопросов со «звёздочкой» для подумать:

int main() {
   double d = 0.56;
   d *= 10;
   // print d, > 5.6

   float f = 0.56;
   f *= 10;
   // print f, < 5.6
}
	cout << setprecision(20);
	fesetround(FE_UPWARD);
	float f = 5.6;
	fesetround(FE_UPWARD);
	cout << f*10 << endl;
	fesetround(FE_DOWNWARD);
	cout << f*10 << endl;

56
55.999996185302734375
kvpfs ★★
()
Ответ на: комментарий от andalevor

Смотря чем ты будешь округлять, так что я думаю это для тебя будет открытие Ceil:

>>> import math
>>> math.ceil(2.5)
3

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

Мне нужно получать 0,0,1,2,3,4,5, а не 5,4,3,2,1,0,0

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

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

у тебя же все равно заранее определено какой разряд куда выводить?

Ну а в принципе да, у меня там есть нечто подобное слотам. Есть слоты 0-9 и в них я кидаю значения. Слоты можно перевернуть, в принципе, задом наперед.

Но я уже не поспал сегодня ночьку ))) И переделал всю «математику» на «целые», без Modulo. Не трогаю дробную часть, просто рассматриваю её как мусор ))) Работает вроде без ошибок, вводишь 56 и на экране тоже будет 56, а не 55 )) Но можно и с Modulo попробовать. Может сократится число вычислений в потоке, что хорошо, но не уверен в этом.

dva20
() автор топика
Последнее исправление: dva20 (всего исправлений: 4)
Ответ на: комментарий от kvpfs

Потому что мантисса сдвигается, хвост теряется (или округляется, зависит от разного)

Да, пусть оно там что угодно сдвигается. Почему это нельзя было учесть и подправить в большую сторону при выдаче результата, чтобы не было бы «комиссии»? )) Выдавало бы в большую сторону, не 5,5999999992, а 5,6000000001

У меня имеется на этот счёт ответ, скорее всего то, что это низкоуровневый тип данных который рассчитан на скорость вычислений, а не на точность, чтобы не сокращать мощности вычислений. Скорее всего это уровень Процессора. А для точных вычислений, уже учли эти все моменты и разработали более высокоуровневые типы данных с плавающей точкой.

Я конечно знал, что float плавающий тип данных, но не знал, что настолько, что невозможно без потери данных сдвинуть запятую )))

dva20
() автор топика
Последнее исправление: dva20 (всего исправлений: 2)
Ответ на: комментарий от dva20

То есть, по сути то я ввёл 10.0, а не 9,9999999. Вот в чём вопрос. Куда C++ дел мои доли? Или почему он их УКРАЛ зараза такой?? Это что за комиссии такие!? ))))))

так это еще с какой стороны посмотреть… Одно дело, если продавать, и совсем другое - если покупать ;) Вообще, если серьезно, все это программирование создали капиталисты, а они имеют тенденцию объегоривать, и этот ментальный фреймворк переносится и на сам процесс создания ПО и компов вообще. Об этом молчат программисты, из корпоративной солидарности, но это так. Если это понять и принять, жить станет гораздо проще.

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

Одно дело, если продавать, и совсем другое - если покупать ;)

Хмм.. Да-да-да :)))

dva20
() автор топика

еще один неосилятор. Дело не в языке, а в архитектуре процессора, который

  1. числа с плавающей точкой представляет в формате IEEE 754
  2. имеет конечную разрядность

Хочешь точные результаты – юзай целые числа (читай – fixed point) и не ной.

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

Хочешь точные результаты – юзай целые числа

Да уже да, спасибо, научился работать с целыми во float типе данных )) Поскольку других типов данных у меня нет.

dva20
() автор топика

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

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

В других языках, которых я немного знаю, Java и Python (да, я понимаю, что это интерпретируемые языки) такого нет, результат всегда ожидаемый:

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

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

Да, пусть оно там что угодно сдвигается. Почему это нельзя было учесть и подправить в большую сторону при выдаче результата, чтобы не было бы «комиссии»? )) Выдавало бы в большую сторону, не 5,5999999992, а 5,6000000001

Потому что там нет 5.6? Процессор тебя нигде не наегоревает, он честно считает что дали, и округляет хвосты согласно заданному режиму. Можешь распринтовать флоат мантиссу, экспоненту и параллельно проверить расчёты, там нет никакой скрытой магии. Что-то там подправлять согласно твоим хотелкам - ну, давай ещё и инты подправлять начнём, если число около некоего круглого, например 999, то можно и 1 добавить.

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

Да никому это особо не нужно. Какую-бы систему ты не взял всё равно будут дыры в числовом ряду, удачи представить 1\3 в десятичной системе, а третичная может. Тормозная фигня для неосиляторов (это при том, что я сталкиваюсь с ценовыми потоками в double’е).

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

Еще разок повозился,

Код на C++ - https://godbolt.org/z/96hnYhsxc Код на Java - https://godbolt.org/z/Tx7PqEvxe

Константа 0.56f введённая в:

c      = 0.56000000238418579102
java   = 0.56000000238418580000
python = 0.56000000000000005329

Результат вычислений 0.56f * 10.0f равен:

c      = 5.59999990463256835938
java   = 5.59999990463256800000
python = 5.60000000000000053291

т.е, C++ и Java взяли «комиссию», а Python - нет, он добавил пожертвования немного )))))

dva20
() автор топика
Ответ на: комментарий от nikolnik

Когда наступит социализм,

В Python уже царит социалка, накидывают немного больше:

c      = 5.59999990463256835938
java   = 5.59999990463256800000
python = 5.60000000000000053291

А С++ и Java просто сдирают конские комиссии с бедного народа!!!

dva20
() автор топика
Ответ на: комментарий от kvpfs

Потому что там нет 5.6?

Так его вообще нигде нет, оно проявляется только на экране, и вот Python могёт вывести 5.6, а другие нет.

Но это так.. пользовательский взгляд, так как в этих трёх языках испольуется float и я это понимаю, что это интерпретируемый язык и в недрах Гвидо ван Руссум «подправил» как «надо».

Но мне неизвестен уровень ассемблера, уровень процессора, я далёк от вычислений в двоичной системе и может даже C++ и Java полностью полагаются на вычисления на стороне только процессора. Но, а что мешало учесть неточность в вычислениях, а вернее не неточность, а потерю данных? Допустим, 10/3 пусть бы там ровнялось 3,3333331110055, и на каких то разрядах точность терялась бы, это вполне понятно. Но при «целочисленных» умножениях происходит потеря данных, но не во всех языках, в Python по итогу моего теста, потери данных нет:

c      = 5.59999990463256835938
java   = 5.59999990463256800000
python = 5.60000000000000053291
dva20
() автор топика
Ответ на: комментарий от anonymous

«бороться и искать, найти и не сдаваться»

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

Но при «целочисленных» умножениях происходит потеря данных, но не во всех языках,

Всё. Я уже поехал ребята ))) По кругу. Ответы были выше верные, вся проблема в двоичности, нет никаких «целых».

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

Вам все ЗАВИДУЮТ! …

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

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

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

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

С компилируемыми ЯП все просто - objdump.

А вот с интерпретируемыми… Тебе придется как-то читать память интерпретатора и соотв. JIT-компилятора, и выуживать из нее искомые команды в нужный момент времени. Возможно, из /dev/mem. Сам поиск в памяти по сигнатуре команд дело тривиальное. Но надо знать, где искать, т.е. смотреть, куда отображаются страницы виртуальной памяти процесса VM или соотв. интерпретатора.

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

Повангую - float питона - это 8ми битный дабл, убери суффикс в f джаве и крестах.

В Java суффикс не уберешь, ругается компилятор на несовместимость типов, а в плюсах убрал, но комиссия не исчезла:

c = 5.59999990463256835938
j = 5.59999990463256800000
dva20
() автор топика
Ответ на: комментарий от seiken

ты уже столько нафлудил тут, что отступать некуда.

Да я уже понял, что вляпался )) Какую ночь уже не сплю! Красные глаза и голодный как волк, ибо никто не приготовит мне кроме меня самого и денег на забегаловки нет ))

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

Это сведёт меня в могилу, чувствую. Надо останавливаться в ресёрче и принять «как есть» и с этим научится жить. )))

dva20
() автор топика
Ответ на: комментарий от kvpfs

Это что получается, С++ комиссию не взял? Возмутительно.

Ну с double то, да, комиссию не берёт, а с float - берёт. )))

P.S. float значения округляют до double

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

Повангую - float питона - это 8ми битный дабл, убери суффикс в f джаве и крестах

Я проверял, float в Python чистейший IEEE, надо было для одной задачи было проверить аппаратное вычисление IEEE действий

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от untitl3d

А куда тебе циклы экономить, если ты уже на жабе? Замедления на обычном жабосервисе не заметишь.

Если складываешь десяток цифор, то нет, если их десятки миллионов, то уже начнешь замечать.

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

В Java суффикс не уберешь

0.56d
Хватит страдать хернёй, иди работай.

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

и вот Python могёт вывести 5.6, а другие нет.

Вот так и становятся говнокодерами, которые носятся со своими «б-жественными» недоязычками.

так как в этих трёх языках испольуется float

В бидоне у тебя double, в си и жабке float.

class Main {
    public static void main(String[] args) {
        double x = 0.56d * 10d;
        System.out.println(x);
    }
}
5.6000000000000005

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

в школе этому учат на уроках математики, а работа с флоат числами в ПК - на уроках информатики.

Тебя этому в школе учили? Просто я такого о школе не помню. Машинной арифметике меня учили уже в институте, а в школе было просто компоюзание и трубопаскакаль

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

я из глубинки урала))).

Привет от северян :)

за комп на информатике сел первый раз только в техникуме, в 1993 году. это был БК0010.

Каюсь - я застал, но школьником ;)

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

У меня на самом деле довольно много вопросов по «звуку» было (сам фанат). Как бы нам privately связаться? Если это уместно, конечно…

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

я тебе больше скажу - в любой финансовой сфере флоатами не пользуются. а если где-то увидишь

Бессовестная ложь. Блин. Нельзя так.

bugfixer ★★★★★
()

Питон не смог …

$ python
Python 3.10.1 (main, Dec 18 2021, 23:53:45) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 0.96*10
>>> print ('%.20f'%a)
9.59999999999999964473
anonymous
()
Ответ на: комментарий от ergo

Деньги флоатами не считают

Кто сказал? У нас цены именно во floats разлетаются, и переходить на double в ближайшее время не предвидится, от слова совсем.

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

У нас цены именно во floats разлетаются, и переходить на double в ближайшее время не предвидится, от слова совсем.

Рисковые вы парни.

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

Деньги флоатами не считают.

Смотря что считать деньгами. Если до миллиона - так это не деньги, а всё что больше, да, не считают.
Если, речь именно про 32-х битный флоат с 7-8 знаками, то нет, конечно, там ничего считать нельзя.

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.