Страницы

Поиск по вопросам

Показаны сообщения с ярлыком double. Показать все сообщения
Показаны сообщения с ярлыком double. Показать все сообщения

вторник, 17 марта 2020 г.

Вывод double числа

#c_sharp #числа #double


Рассчитываю pi
3.141592653589794 - это я вижу в отладчике
3.14159265358979  - это, когда вывожу 
Как мне вывести число полностью?    


Ответы

Ответ 1



Советую ознакомиться с вариантами форматирования вещественных чисел. Для вашего случая подходит описатель формата "R", который гарантирует идентичное число при обратном преобразовании (double.Parse()) Console.WriteLine(pi.ToString("R"));

воскресенье, 8 марта 2020 г.

Как корректно отобразить переменную double в java

#java #console #double


При нахождении скорости целыми числами вывод отображается корректно, но если задать,
допустим время 7,6 , то компилятор пишет ошибку    

import java.util.*;
import java.lang.*;
import java.io.*;
public class Speed {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    // TODO Auto-generated method stub
    System.out.println("Давай-ка посчитаем твою скорость");
    System.out.println("Введи время и дистанцию");
    System.out.println("Введи дистанцию в метрах");

    double distance = sc.nextDouble();
    System.out.println("Хорошо, теперь время");
    double time = sc.nextDouble();
    System.out.println("");
    double speed = ((distance/time)*3600/1000);
    double speed1 = (distance/time); 
    System.out.format("Твоя скорость равна ");
    System.out.format("%.2f", speed);
    System.out.println(" км/ч");
    System.out.print("или ");
    System.out.format("%.2f", speed1);
    System.out.print(" м/с");
  }
}

    


Ответы

Ответ 1



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

суббота, 7 марта 2020 г.

C#. Как преобразовать число с плавающей точкой в аналогичное число, но только с заданным количеством знаков после запятой?

#c_sharp #net #double


Доброго времени суток! 

Возник следующий вопрос: к примеру, у нас есть переменная типа double и некоторая
точность (переменная precition), которая будет определять количество интересующих нас
знаков после запятой:

    double x = 9.3813020199999;
    double precition = 0.001;


Зная вышеперечисленные данные, нам нужно получить точное число 9.381 (и без лишних
нулей в конце). Есть ли способ это осуществить?

UPD Мне необходимо протестировать метод Sqrt (код ниже) и я не знаю, как правильно
написать тест для NUnit, если метод возвращает число double. Ведь у нас должно быть
expected-значение, которое сравнивается с возвращаемым значением метода.

    public static double Sqrt(double x, int n, double precition)
    {
        if (x < 0 && n % 2 == 0)
            throw new ArgumentException();

        double result = x / n;
        double previousResult;
        do
        {
            previousResult = result;
            result = ((double)1 / n) * ((n - 1) * previousResult + (x / Sqr(previousResult,
n - 1)));
        }
        while (Math.Abs(result - previousResult) > precition);

        return result;
    }


Пока мой тестирующий метод, написанный с помощью NUnit, выглядит так:

    [TestFixture]
    public class NewtonSqrtTests
    {
        [TestCase(4, 2, 0.001, ExpectedResult = 2.000)]
        [TestCase(27, 3, 0.0001, ExpectedResult = 3.0000)]
        [TestCase(88, 2, 0.001, ExpectedResult = 9.380)]
        [TestCase(81, 2, 0.001, ExpectedResult = 9.000)]
        public double Sqrt_PositiveTest(double x, int n, double precition)
        {
            return NewtonSqrt.Sqrt(x, n, precition);
        }
    }

    


Ответы

Ответ 1



Если говорить о десятичных знаках, то оставаться в рамках типа double бессмысленно: в нём вы не можете выразить вашу требуемую точность точно. Например, потому, что в типе double нельзя точно выразить ни число 0.001, ни число 9.381. (иллюстрация, связанный ответ) Вам нужно перейти к типу decimal, который специально для этого предназначен. double x = 9.3813020199999; decimal precision = 0.001m; decimal result = Math.Round((decimal)x / precision) * precision; // 9.381 Если количество десятичных знаков известно в виде числа, можно проще: double x = 9.3813020199999; decimal result = Math.Round((decimal)x, 3); // 9.381 Обновление: Если оставаться в рамках типа double, вы не можете их сравнивать: равенство теоретически одинаковых чисел типа double, вычисленных разными путями — практически невероятное событие из-за ошибок округления. В таких случаях используется примерное равенство с точностью до epsilon. Например, в NUnit есть для таких целей специальные функции: Values of type float and double are normally compared using a tolerance specified by the Within modifier.

среда, 4 марта 2020 г.

Остаток от деления с fmod

#php #double #cpu


Необходимо проверять кратность количества и коэффициента. Казалось, остаток от деления
должен быть 0, но нет, выводит некоторое значение. 

Почему? Как сделать, чтобы в таких случаях корректно считал? 

Сейчас сделал временное решение с round(fmod,5). 

$count = 46;
$k = 4.60;

echo fmod($count, $k);


Ответ 3.5527136788005E-15
    


Ответы

Ответ 1



Сегодня разбирали похожий случай с другой функцией и в другом языке, но имеющий причиной, фактически, то же самое: Непонятный результат при системном разделителе «точка» Как показали комментарии ниже, предложенный мною ранее вариант не дает 100%-ного профита. Тогда, с учётом всех комментариев, наверное, как-то так (прототип нашел у себя в include):

Ответ 2



Проблема связана с тем, как устроены числа с плавающей запятой. Начать с того, что 46 в двоичной системе может быть представлено точно, в то время как 4,6 — не может, и в действительности хранится как 4,59999999999999964472863211995. В вашем конкретном случае речь идёт об одной операции деления. Операции в стандарте IEEE 754 разработаны так, чтобы не вносить ошибку больше, чем половина ULP. (Это не относится к вычитанию, но углубляться не буду, поскольку здесь не вычитание). Значит, после единственного деления ошибка может отличаться от 0.0, но будет меньше, чем ULP/2. ULP (Unit in the Last Place) — единица в последнем знаке. Это не константа, она отличается для разных чисел, поэтому мы будем обозначать её ULP(x), то есть это функция от числа x. Нам надо посчитать ULP для остатка от деления 46 на 4,6. Поскольку результат сравним c 4,6 (то есть находится в диапазоне от 0 до 4,6) мы как раз хотим посчитать ULP(4,6). Двоичное представление числа 4,6 равно 1,00100110011001100110011001100110011001100110011001102×22. У чисел с двойной точностью под мантиссу выделено 52 бита, следовательно ULP(4.6) равно 0,00000000000000000000000000000000000000000000000000012×22. Константа слева от знака умножения называется машинным эпсилоном, она равна 2-52. Показатель степени 2 в формуле 22 — двоичная экспонента числа 4,6. В C/C++ для её вычисления можно вызывать функцию frexp, но в PHP её нет, так что надо заменить на floor(log10($x)/log10(2)). Сводим всё воедино. При вычислении остатка от деления $n на $m ошибка может составить ULP($m)/2. $epsilon = pow(2, -53); $binaryExponent = floor(log10(abs($m))/log10(2)); $ulp_m = $epsilon = pow(2, $binaryExponent); Вычислив остаток, сравниваем его с величиной ULP/2. Если он меньше, значит принимаем его равным нулю. $remainder = fmod($n, $m); if (abs($remainder) < $ulp_m/2) { $remainder = 0.0; } Я расставил в коде вызовы abs, чтобы он работал и с отрицательными числами тоже. Если вам нужно просто вывести число, забудьте всё, что написано выше и ограничьте количество выводимых цифр. sprintf('%.1f', fmod($n, $m))

вторник, 25 февраля 2020 г.

Переполнение типа double при вычислении математических функций

#cpp #double


Помогите разобраться. У меня есть код, в котором я подключаю проверку на переполнение
типа double с помощью функции _finite() относительно математических операций, а не
через присваивание переменных. В консоли для z выводит ошибку OVERFLOW error. Возможно
ли в этом случае обойтись без обработки исключений? Компилятор - C++Builder 6. Буду
очень благодарен.
#include 
#include 
#include 
using namespace std;

int main (void)
{
    double x = 10E200;
    double y = 10E400;
    double z = pow(x, 2);
    if(_finite(x) != 0)
    {
        cout << "This is finite number: " << x << endl;
    }
    else cout << "This is infinity: " << x << endl;
    if(_finite(y) != 0)
    {
        cout << "This is finite number: " << y << endl;
    }
    else cout << "This is infinity: " << y << endl;
    if(_finite(z) != 0)
    {
        cout << "This is finite number: " << z << endl;
    }
    else cout << "This is infinity: " << z << endl;
    system("pause");
}

Update: Это моя вина, многие не поняли, чего я хотел, поэтому добавлю второй пример.
Через pow() или x * x выводит ошибку о переполнении. Как мне корректно проверить условие,
чтобы не выводило ошибку?
#include 
#include 
#include 
using namespace std;

int main (void)
{
    double x;
    char str[] = "Введите число, которое мы будем возводить в степень: ";
    CharToOem(str,str);
    cout << str;
    cin >> x;
    int check = 0; // флаг переполнения
    do
    {
        if(_finite(pow(x, 2)) != 0)
        {
            x = pow(x, 2);
            cout << x << endl;
        }
        else check = 1;
    }
    while(check == 0);
    system("pause");
}
    


Ответы

Ответ 1



Через pow() или x * x выводит ошибку о переполнении. Как мне корректно проверить условие, чтобы не выводило ошибку? Если я ничего не путаю: ( a * e ^ x ) * ( b * e ^ y ) = c * e ^ ( x + y + z ), где z - экспонента получаемая при перемножении a на b, а c - остаточная мантисса от этой операции. Т.е. чтобы определить, будет ли переполнение, можно просто умножить целочисленную экспоненту на n (показатель степени - второй параметр функции pow) и проверить, выходит ли это значение за пределы используемого диапазона ( для double - это 1024). Если не выходит, нужно выделить остаточную экспоненту от квадрата мантиссы, добавить к ней вычисленное ранее произведение и проверить на выход за границы снова. Вобщем - достаточно муторно, но не смертельно. Чтобы выделить остаточную экспоненту, нужно выполнить целочисленное умножение 64-битных мантисс. Предварительно, нужно маской удалить из них экспоненту (те самые 11 бит) и привести обратным кодом в натуральный диапазон. После умножения - обнулить старший бит, и посчитать число правых сдвигов результата до того момента, пока эти 11 бит не обнулятся. Число этих сдвигов и дают остаточную экспоненту. p.s.: экспоненты - тоже знаковые, это следует учитывать при их сложении, вернее - при проверке на выход за пределы границ диапазона (для этого, надо перевести 11-битную экспоненту в short, заполняя оставшиеся 5 старших бит значением старшего бита этой экспоненты).

воскресенье, 9 февраля 2020 г.

Приведение числа double к float с большой точностью

#cpp #float #double


Пусть у меня есть некоторое значение типа double, мне необходимо провести некоторые
манипуляции над этим значением и конвертировать в тип float.
Имею такие данные:

double x = 12058660.309519000;
float xx = x * 0.1f;

когда я проверяю через отладку выражение для переменной xx получаю - 1205866.0309518999,
но когда проверяю само значение переменной xx, то получаю - 1205866.00

Почему?

Мне необходимо сохранить точность.
    


Ответы

Ответ 1



Вы уперлись в точность. Точность float — 7-8 знаков. Это значит, что float может хранить только 7 или 8(нечет и чет соответственно) цифр. Возьмите число с целой частью поменьше(1234, допустим), и у вас останется еще 8-4==4 цифры для дробной части. Точность double(число двойной точности) - 15 знаков. Как можете заметить, double у вас тоже округляется до 15-го знака, причем не после запятой, а вообще. Округление связано с тем, что в памяти компьютера все числа представляются в двоичной системе счисления. Поэтому дробные числа в большинстве своем представляются в виде дроби, а уже она потом округляется, что вы и видите. В float вы не сможете хранить больше 7-8 цифр, повторяю еще раз.

Ответ 2



Проведем простой эксперимент #include #include int main() { float f = 1205866; printf("%.10f\n", f); printf("%.10f .. %.10f\n", nextafterf(f, 0), nextafterf(f, 1e8f)); } Получаем 1205866.0000000000 1205865.8750000000 .. 1205866.1250000000 Это - ближайшие соседние числа, представимые в IEEE 754 типе float. Никакие другие значения в этой окрестности тип float представить не может. То есть ни о каком 1205866.0309518999 не может быть и речи. 1205866 - это ближайшее к вашему 1205866.0309518999 представимое значение типа float. Вот и ответ на ваш вопрос "Почему?". Таким образом вы прекрасно "сохранили точность" - настолько, насколько это вообще возможно в выбранном вами типе. Если вы хотите еще лучше "сохранить точность", то тип float вам тут помочь не сможет. Используйте более точные типы. А если вам абсолютно необходимо конвертировать результат в тип float, то - увы... Например, аналогичный эксперимент с типом double даст нам 1205866.00000000000000000000 1205865.99999999976716935635 .. 1205866.00000000023283064365 Как видите, "шаг" типа double в этой окрестности несравнимо мельче, чем ваше .0309....

Почему Float.MAX_VALUE-1 == Float.MAX_VALUE – true?

#java #float #double



Почему Float.MAX_VALUE-1 == Float.MAX_VALUE – true?
Какие максимальные числа для float и double, с которыми такой проблемы не будет?

    


Ответы

Ответ 1



Для начала рассмотрим аналогию, которая пояснит ситуацию. Представьте, что вы имеете возможность хранить только 3 десятичные цифры и вам нужно записать число 12345. Не получится, но вы можете сохранить его в другом формате: 123 * 10**2. Именно так (упрощённо говоря) и работает арифметика с плавающей запятой: хранятся только самые старшие биты числа и порядок (сдвиг, степень, экспонента - как хотите, так и называйте). Теперь вы хотите отнять единицу. Ну давайте попробуем вместе: 12345 - 1 = 12344. Однако вы можете хранить только 3 цифры, стало быть, имеем 123 * 10**2, а это в точности то же самое, что было! То есть вычитание единицы не влияет на ситуацию, потому что единица влияет на те цифры, которые не хранятся, она как бы слишком маленькая. Подробнее об этом есть здесь. Там на примере игрушечного формата чисел с плавающей запятой идёт рассказ об этом и некоторых других неожиданных эффектах. Далее вы спрашиваете какое максимальное число этим свойством не обладает. Коль скоро у типа float 23 бита отводится под цифры (мантисса), а ещё один бит всегда единица, мы получаем, что из чисел вплоть до 2**24 можно вычитать единицу и она БУДЕТ вычитаться корректно. Однако далее между 2**24 и 2**25 числа идут с шагом два, поэтому каждое второе при вычитании единицы будет посередине между чётным и нечётным числами, а потому по правилу округления nearest-to-even, half-to-even число будет округляться вниз (к чётному). Таким образом, на этом интервале все ЧЁТНЫЕ числа, НЕ делящиеся на 4, будут уменьшаться на 2 при попытке отнять 1. Остальные числа НЕ будут уменьшаться. Например, число 33554430 (не делится на 4) станет равным 33554428, однако при повторном вычитании 1 ничего не изменится. Кстати, число 33554430=2**25-2 и есть максимальное, о котором вы просите. Однако если вам нужно, чтобы и предыдущее (полученное после вычитания 1) тоже нормально вычиталось, то тогда наибольшим будет 2**24+2. После вычитания 1 вы получите 2**24 (то есть оно уменьшится на 2) и далее при вычитании 1 будет уменьшаться на 1. Для double это будет 2**53+2 = 9007199254740994 (если нужно, чтобы все предыдущие вычитались нормально) и 2**54-2 = 18014398509481982 (если после вычитание 1 оно уменьшится на 2 и повторное вычитание не даст результата).

Ответ 2



Можно вот так проверить: public static void main(String[] args) { float i; for (i = 1; i < 1e38; i *= 10) { System.out.println(i + " " + (Float.MAX_VALUE-i == Float.MAX_VALUE)); } }

суббота, 8 февраля 2020 г.

String to double в java

#java #строки #float #double


Имеется строка вида: 122.32,20.543. Каким образом в  java распилить ее чтобы получить
два double типа double1 = 122.32 и double2 = 20.543?
    


Ответы

Ответ 1



Разделить по запятой и каждую подстроку преобразовать в double. String[] tokens = "122.32,20.543".split(","); Double double1 = Double.valueOf(tokens[0]); Double double2 = Double.valueOf(tokens[1]); Для решения достаточно посмотреть на документацию к классам String и Double.

Ответ 2



а я опять изобрел велосипед((( String s = "122.32,20.543"; Double d1 = Double.valueOf(s.substring(0, s.indexOf(","))); Double d2 = Double.valueOf(s.substring(s.indexOf(",")+1)); System.out.println("Число 1 = "+d1 + "\nЧисло 2 = "+ d2);

среда, 29 января 2020 г.

Вывод double в java

#double #java


Имею: 
double s1 = 2.0;
double s2 = 2.3;
double s3 = 2.33,
double s4 = 2.333;

Требуется чтоб при выводе на экран вышли результаты: 
s1 = 2
s2 = 2.3
s3 = 2.33
s4 = 2.333

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


Ответы

Ответ 1



Formatting Numeric Print Output Код на ideone.com /* package whatever; // don't place package name! */ import java.util.*; import java.lang.*; import java.io.*; /* Name of the class has to be "Main" only if the class is public. */ class Ideone { public static void main (String[] args) throws java.lang.Exception { double s1 = 2.0; double s2 = 2.3; double s3 = 2.33; double s4 = 2.333; System.out.format("s1 = %.0f%n", s1); System.out.format("s2 = %.1f%n", s2); System.out.format("s3 = %.2f%n", s3); System.out.format("s4 = %.3f%n", s4); System.out.format("s1 = %.0f; s2 = %.1f; s3 = %.2f; s4 = %.3f", s1, s2, s3, s4); } }

Ответ 2



Спасибо, я сам нашел уже) 10 раз заходил по первой ссылке, но не дочитывал до конца) Вот код, который решил мою проблему. import java.text.*; public class DecimalFormatDemo { static public void customFormat(String pattern, double value ) { DecimalFormat myFormatter = new DecimalFormat(pattern); String output = myFormatter.format(value); System.out.println(value + " " + pattern + " " + output); } static public void main(String[] args) { customFormat("###,###.###", 123.0); customFormat("###,###.###", 123.120); customFormat("###,###.######", 123.0); customFormat("###,###.######, 123.123120); } } Результат: 123 123,12 123 123,12312

пятница, 10 января 2020 г.

Примитивные типы Java float и double

#java #float #double


Кто может подсказать максимальные размеры для float и double по вики:

float   32  от 3.4е-038 до 3.4е+038
double  64  от 1.7е-308 до 1.7е+308

По этому коду:

   System.out.println("MAX FLOAT : "+Float.MAX_VALUE);
   System.out.println("MIN FLOAT : "+Float.MIN_VALUE);
   System.out.println("MAX Double : "+Double.MAX_VALUE);
   System.out.println("MIN Double : "+Double.MIN_VALUE);


MAX FLOAT : 3.4028235E38
MIN FLOAT : 1.4E-45
MAX Double : 1.7976931348623157E308
MIN Double : 4.9E-324

Вопрос следующий, где правда?
    


Ответы

Ответ 1



Смотрите. Есть разница между минимальным нормализованным и ненормализованным значениями. 1.1754943508222875E-38f — это минимальное нормализованное, 1.401298464324817E-45f — минимальное ненормализованнoе значение для float. Вот официальная документация. Немного теории. Обычно числа с плавающей запятой записываются внутри в виде мантисса + экспонента, где мантисса — это число от 0.1 до 1, а экспонента — показатель степени десятки. Например, число 0.0015 содержится как 0.15 + степень 10^-2, 44 — как 0.44 и степень 10^2. (На самом деле там то же самое в двоичном виде, степени двойки и числа между 1 и 2^-1. Но важен принцип.) Под мантиссу отводится фиксированное количество разрядов, стартовая цифра выбрасывается для экономии (потому что она должна быть равна 1 в двоичной системе). Так вот: можно немного выйти за нижнюю грань, используя денормализованные числа — числа наподобие 0.0000156 * 10^-38. У них точность хуже, чем у нормальных чисел, т. к. старшие разряды мантиссы используются для укказания величины. Но это лучше, чем просто 0.

вторник, 31 декабря 2019 г.

Как сравнить float и double?

#cpp #float #сравнение #double


Как правильно сравнивать два числа типа float и double? Следующий способ часто говорит,
что одинаковые числа различны:

float a = 0.00001001;
double b = 0.00001001;

if (a == b) {
  std::cout << "equal"; // не выводит equal
}


Мой вопрос отличается от дубликатов тем, что мне нужно знать, как правильно сравнить
2 числа типа float и double на C++, а не почему (не только почему) простое сравнение
не работает. В привидённых в дубликатах ответах либо ответы для 2 одинаковых типов,
либо не сказано как выбирать epsilon и т.д..
    


Ответы

Ответ 1



if (fabs(a - b) <= eps) где eps - некая маленькая величина, вообще говоря - зависящая от порядка самих чисел, поэтому более корректно if (fabs(a - b)/ max(fabs(a) + eps, fabs(b) + eps) <= eps) Пример задания eps: const FuzzFactor = 1000; SingleResolutionEps = 1E-7 * FuzzFactor; DoubleResolutionEps = 1E-15 * FuzzFactor; Откуда это берётся - точность float 23 двоичных разряда или 7-8 десятичных, т.е. число имеет 7 верных десятичных цифр, поэтому меньше 1E-7 eps смысла нет делать. Почему используется множитель FuzzFactor = 1000; - это расширение допуска, чёткого критерия его выбора нет, разработчики этой библиотеки решили так сделать, а вообще множитель можно выбирать в зависимости от желаемой погрешности в младших разрядах. Для сравнения float и double по правилам сложения погрешностей следует использовать погрешность для менее точных float, т.е. порядка 1E-7 даже при приведении float к double, как предложил в комментарии @Grundy - ведь при этом приведении возникают дополнительные ничем не обеспеченные разряды с погрешностью в пределах всё тех же 1E-7. Труды по точности float-арифметики: 1 2 Goldberg

Ответ 2



Машинный эпсилон это одно,а точность входных данных в задаче это совсем другое. Редко бывает, чтобы точность входных данных в задаче была равна машинному эпсилону. Обычно точность входных данных в задаче гораздо меньше, чем машинный эпсилон. Есть целая наука об правилах округления и отбрасывания незначащих цифр. Исходя из этой науки и выбирается в каждом конкретном случае эпсилон для каждой конкретной задачи. А машинный эпсилон это характеристика данной вычислительной системы.

понедельник, 30 декабря 2019 г.

Множественные ошибки вычислений с плавающей точкой

#cpp #c #double


Есть функция 

inline unsigned long long d(double d)
{
    return (864E9 * d + 0x014f35a9a90cc000 - 0x019DB1DED53E8000) / 10;
}


При ее работе вылетает такая ошибка(код 0xC00002B4)


  Множественные ошибки вычислений с плавающей точкой (parameters: 0x00000000, 0x000005A0).


Самое интересное что эта функция вызывается дважды с одним и тем же входным параметром.
И такая ошибка появляется при повторном вызове. Я пробовал сделать семпл в отдельном
проекте но воспроизвести ошибку не получилось.

Что эта ошибка вообще означает? и как это можно исправить?

PS. VisualStudio 2015

Настройки проекта(Code Generation)

Smaller Type Check: No
Basic Runtime Checks: Default
Runtime Library: /MDd
Floating Point Model: Precise (/fp:precise)


PS2. Если переписать функцию так:

inline unsigned long long GetTsFromDate(double date)
{
    unsigned long long tmp = 0x014f35a9a90cc000 - 0x019DB1DED53E8000;
    double dbl = 864E9 * date;
    double dbl2 = dbl + tmp;
    double result = dbl2 / 10;
    return result;
}


То будет падать на double dbl = 864E9 * date
    


Ответы

Ответ 1



Провёл несколько экспериментов в Visual Studio 2010. Создал консольное приложение с настройками по умолчанию (в частности, модель вычислений с плавающей точкой /fp:precise, исключения с плавающей точкой отключены /fp:except-). Все примеры выполнялись в Debug-режиме. Воспользуемся функцией _statusfp2 для того чтобы узнать floating-point status word в следующем простом примере: #include #include #include using namespace std; #pragma fenv_access (on) int main() { cout.precision(numeric_limits::max_digits10); unsigned int x86, SSE2; double d; _statusfp2(&x86, &SSE2); cout << x86 << " " << SSE2 << endl; d = 0.0; d = d * 1.0; _statusfp2(&x86, &SSE2); cout << d << endl; cout << x86 << " " << SSE2 << endl; return 0; } В моём случае, программа выдала следующий результат: 0 0 0 0 0 Насколько я понял, нулевые значения переменных x86 и SSE2 означают, что при вычислениях с плавающей точкой никаких исключительных ситуаций не произошло. Это логично, ибо никаких "опасных" вычислений в данном пример не осуществляется. Воспользуемся примером из вашего вопроса: d = 864E9; d = d * 43083.341116850243; _statusfp2(&x86, &SSE2); cout << d << endl; cout << x86 << " " << SSE2 << endl; В моем случае программа выдала следующий результат: 37224006724958608 1 0 В приведённом выводе программы есть два важных момента. Во-первых, результат перемножения двух чисел 864E9 и 43083.341116850243 получился не точным. Это легко проверить перемножением этих чисел на калькуляторе: точный результат содержит 20 значащих цифр. Такое число не может быть представлено в переменной типа double. Во-вторых, изменилось состояние floating-point status word. Судя по справке, status word приняло значение _EM_INEXACT. То есть имела место исключительная ситуация — результат арифметической операции не может быть точно представлен в переменной типа double. Тем не менее, программа завершила свою работу нормально, никакого исключения сгенерировано не было. Это обусловлено тем, что по-умолчанию исключения с плавающей точкой замаскированы. Воспользуемся функцией _controlfp_s для того, чтобы демаскировать исключение _EM_INEXACT: unsigned int currentControl _clearfp(); _controlfp_s(¤tControl, 0, 0); _controlfp_s(nullptr, currentControl & ~static_cast(_EM_INEXACT), _MCW_EM); d = 864E9; d = d * 43083.341116850243; В этот раз на строке d = d * 43083.341116850243; выскакивает ошибка: 0xC000008F: Floating-point inexact result. В настройках проекта включим расширенный набор инструкций /arch:SSE2 и на той же самой строке выскакивает немного другая ошибка: 0xC00002B4: Множественные ошибки вычислений с плавающей точкой. А это весьма похоже на ошибку в вашем вопросе. Полагаю, где-то в вашем проекте по какой-то причине демаскируются исключения с плавающей точкой, и обратная маскировка не производится. Помимо исключения _EM_INEXACT есть также исключения _EM_INVALID, _EM_DENORMAL, _EM_ZERODIVIDE, _EM_OVERFLOW и _EM_UNDERFLOW. Замаскировать их все можно следующим образом: _clearfp(); _controlfp_s(nullptr, _MCW_EM, _MCW_EM); P.S. При манипулировании floating-point status word прагма #pragma fenv_access (on) обязательна, иначе компилятор может проигнорировать манипуляции. Также, если всё-таки хочется обрабатывать исключения с плавающей точкой, то судя по всему необходимо использовать опцию /fp:except, иначе может что-нибудь сломаться. Но тут я не уверен.

Ответ 2



В вашей второй функции первая строка меня очень смущает. unsigned long long tmp = 0x014f35a9a90cc000 - 0x019DB1DED53E8000; Зачем вы присваиваете unsigned long число, которое заведомо меньше нуля? Тут даже видно, 0х014 - 0х019. И зачем вам long long, когда хватит обычного long? У меня при выполнении этого действия(отладка) tmp == 18424652457709551616 Далее, смотрю на первую функцию. Вижу что-то вроде переполнения. 864E9 * d + 0x014f35a9a90cc000 здесь вы к double результату 864E9 * d прибавляете 0x014f35a9a90cc000, а второе число уже длиннее пятнадцати значащих знаков double, вряд ли вы контролируете это переполнение. (у double после 15-го знака идут нули) Ну и далее, от сомнительного результата предыдущего действия вы отнимаете еще один long, значение которого опять же больше double. Вот вам и множественные ошибки вычислений. По умолчанию такие ошибки(исключения) скрыты, у вас, видимо, они включены.

Вывод double с нужной точностью и в нужном формате

#cpp #строки #stl #double #format


Требуется вывести double с максимальной точностью, при этом целую часть, которая
не превышает 999 вывести с пробелами на месте отсутствующих цифр, типа 9,12...

Вывожу следующим образом

std::cout << std::fixed << std::setprecision(std::numeric_limits::digits10
+ 1) << value << std::endl;


Возникло 2 проблемы:

1) везде читал, что точность double определяется как std::numeric_limits::digits10,
однако похоже, что можно вытащить больше значащих цифр, если задать точность в 20 цифр,
то будут видны все 20 отличных от 0 цифр

2) не нашел, как можно задать размер целой части, чтобы заполнить отсутствующие цифры
пробелами

Подскажите, что требуется сделать?
    


Ответы

Ответ 1



Всё, чего вам не хватало, это задать заполнитель setfill и ширину setw выводимого поля: #include #include #include void print(double value) { const auto digits = std::numeric_limits::digits10; std::cout << std::setfill(' ') << std::setw(digits + 4); std::cout << std::fixed << std::setprecision(digits) << value << std::endl; } int main() { double value = 9.12; print(value); } Результат выполнения

Ответ 2



Можно также написать класс, с функциональностью выводить как угодно. Например: #include #include #include using namespace std; class IM { double d; size_t ww; char c; public: IM(double val, size_t whole_size, char imbue = ' ') : d(val), ww(whole_size), c(imbue) {} int get_whole() const { return d; } int get_fraction() const { stringstream s; s << d - get_whole(); int k; s.ignore(2); // пропускаем '0' и '.' s >> k; return k; } friend ostream& operator <<(ostream& os, const IM& m) { os << setw(m.ww) << setiosflags(ios_base::left) <

Ответ 3



Во-первых, смысл величины digits10 для плавающего типа T заключается в том, что если вы возьмете десятичное строковое представление, преобразуете его в значение типа T, а затем преобразуете его из T обратно в десятичное строковое представление, то вы получите digits10 значащих цифр цифр, совпадающих с вашей исходной строкой. Например, если вы используете плавающий тип для хранения целых значений, то целые значения с таким количеством цифр будут представляться без потерь, как и соседние (+-1) целые значения. Родственной величиной является величина max_digits10, которая говорит, что если вы преобразуете плавающее значение типа T в десятичное строковое представление с сохранением max_digits10 старших значащих цифр, а затем преобразуете его обратно в тип T, то вы гарантированно получите исходное значение типа T. Таким образом, величина digits10 описывает сохранение данных в преобразовании туда-обратно вида "строка10 -> плавающее -> строка10". А величина max_digits10 описывает сохранение данных в преобразовании туда-обратно вида "плавающее -> строка10 -> плавающее". Это, однако, совсем не означает, что десятичное строковое плавающее значение будет иметь только столько точных цифр. Огульно обзывать цифры за пределами этого количества "мусором" - очевидная профанация. Какие цифры являются точными, а какие нет - определяется спецификой ваших вычислений и известно только вам. Очевидный пример: "традиционные" двоичные плавающие типы способны точно представлять степени двойки в пределах возможностей экспоненты. То есть std::pow(2, 512) даст вам точное значение типа double 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096 в котором намного больше значащих цифр, чем digits10 или max_digits10 для double (15 и 17 соответственно). Никакого "мусора" в этом представлении нет (разумеется, если вы хотели вычислить именно 2512) . Вас это не должно удивлять. Умение пользоваться такими возможностями плавающих представлений - это во многом и есть умение пользоваться плавающими типами в общем. Во-вторых, что касается управления шириной поля при выводе - об этом вы уже получили ответы.

Ответ 4



По ходу обсуждения оказалось, что имеет смысл остановиться подробнее на определении точности вещественного типа. Удивительно, но оказывается, что точность double совсем не жалкие 15 цифр, а намного, намного больше! И в подтверждение приводятся числа 2^(-100) и 2^512, которые действительно представляются точно. Так что же, получается что и в самом деле точность double больше, чем 15 цифр? К сожалению нет, такие заявления говорят лишь о непонимании вещественной арифметики. Давайте посмотрим на представление чисел вещественным типом. Точность представления определяется его мантиссой, которая в случае числа двойной точности имеет длину 52 бита и соответственно может представить 2^52 значений. Теперь возьмем некое число, у которого в двоичном представлении биты мантиссы 53, 54, и т.д. являются нулями. Нетрудно видеть, что такое число будет представлено абсолютно точно. С бесконечной точностью! И таких чисел существует ровно 2^52. Попробуем теперь немного уменьшить требования к точности. Возьмем, скажем, тысячу знаков после запятой - есть ли числа, которые представляются с такой точностью? Да, такие числа в самом деле есть, и из свойств множества действительных чисел видно, что их бесконечно много. Какую бы точность мы ни задали - сто, тысячу, миллион знаков после запятой, - оказывается, что существует бесконечно много чисел, представимых именно с такой точностью. Получается, что точность вещественного типа это какая-то странная характеристика, которая произвольно меняется в зависимости от числа вплоть до бесконечности? Нет, именно в этом месте и происходит путаница! Точность представления отдельно взятого числа не имеет никакого отношения к точности вещественного типа. Слово одно, но под ним подразумеваются совершенно разные вещи. А собственно точность представления числа, хотя и кажется очень важной с бытовой точки зрения, в вещественной арифметике никакого значения не имеет. По той простой причине, что она непредсказуема - мы не можем определить точность представления по результату вычислений. Для этого надо этот результат сравнить с эталонным значением, а если мы и так его знаем, то зачем нам что-то вычислять? Так что же такое точность вещественного типа? Представим, что у нас есть два числа, у которых первые 52 бита мантиссы совпадают, а различается бит 53. Эти два числа будут представлены одной и той же мантиссой, и соответственно, тем же самым числом двойной точности. Мы говорим, что точность double составляет 52 бита, и это означает простую вещь: double различает только числа, у которых отличаются первые 52 бита. Числа, у которых отличаются только биты 53 и последующие, представляются одной и той же мантиссой. Вот это и есть точность! Соответственно,в десятичном виде точность double составляет 15.45 десятичных цифр. Это означает, что числа, отличающиеся первыми 15 знаками всегда представляются разными значениями double. Числа с отличиями в 16-ом знаке могут представляться разными значениями (в 45% случаев), а могут и не отличаться (соответственно, в 55% случаев). Числа с отличием в 17-ом и более младших знаках имеют одинаковое представление в числе двойной точности. Разумеется, тема точности вещественной арифметики немного сложнее этого простого определения. Для более серьезного ознакомления можно посоветовать Д. Кнут, том 2 "Получисленные алгоритмы", глава 4.2.2 "Точность выполнения арифметических действий в системе с плавающей точкой".

среда, 18 декабря 2019 г.

Конвертировать double в int

#java #double


Подскажите, как конвертировать double в int?
    


Ответы

Ответ 1



Сама конвертация не сложна: double doubleValue = 0.0001; int value = (int) doubleValue; Т.е. инструкция в общем виде выглядит так: type v1 = (type) v2; Но тут, есть подводные камни, а именно - тип int содержит значения в диапазоне от -2147483648 до 2147483647, притом что они являются целочисленные, т.е. без дробной части. А double содержит числа в диапазоне от 4.9E-324 до 4.9E-324. Т.е. при конвертации может произойти переполнение и/или не учитывание дробной части. Для более гибкого преобразования стоит использовать классы BigDecimal и BigInteger.

Ответ 2



Если нужно округлять вверх или вниз, используйте Math.round() double a = 1.8; int b = Math.round(a); // b = 2

Ответ 3



Double mDouble = 0.25; int mInt = (int)mDouble;

Ответ 4



Округляем в нужную сторону. double dx = 10.787901; double newDouble2 = new BigDecimal(dx).setScale(3, RoundingMode.HALF_EVEN).doubleValue(); int ix = (int)newDouble2; // переводим в int UP — округление в сторону большего числа для положительных чисел и меньшего для отрицательных. DOWN — округление в сторону меньшего числа для положительных чисел и большего для отрицательных. CEILING — округление в сторону большего и для положительных, и для отрицательных чисел. FLOOR — округление в сторону меньшего и для положительных, и для отрицательных чисел. HALF_UP — округление в большую сторону в случае числа вида 0.5 HALF_DOWN — округление в меньшую сторону в случае числа вида 0.5 HALF_EVEN — классическое округление Ссылка на документацию >>>

воскресенье, 15 декабря 2019 г.

Как получить число идущее после запятой введённой переменной типа double в C++? (например, ввели 14.25 - вывело 25)

#cpp #double


Примерно продумал синтаксис, но выводит число неправильно. Алгоритм такой: число
оставить без его целой части, далее умножать на 10 до тех пор, пока разность nmb -
(int)nmb не окажется равной нулю.

#include
#include
using namespace std;

void main()
{
double nmb;

cin >> nmb;

// Кол-во чисел после запятых равно: 

cout << (nmb - (int)nmb) << endl;

// Само число равно

while (nmb - (int)nmb != 0)
{
    cout << nmb << endl;
    nmb *= 10;
}

cout << "Result number is " << nmb << endl;

system("pause");
}




Мне нужно просто то, что:

1) Убирает лишние цифры/нули (желательно объяснение)

2) Не является чем-либо "заумным" (например, не функции из языка Си)

И ещё нужен тот, кто:

1) Поможет понять причину того, почему у меня код не работает.

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

3) Скажет, есть ли что-то проще ваших Сишных функций: поймите, я новичок.
    


Ответы

Ответ 1



Скажите, а int nmb, frc; char dot; cin >> nmb >> dot >> frc; вас не устроит? После этого то, что вам нужно - будет в frc... Проблема одна - что вы хотите получить для 14.0025 - тоже 25?

Ответ 2



int fractional_part_as_int(double number, int number_of_decimal_places) { double dummy; double frac = modf(number,&dummy); return round(frac*pow(10,number_of_decimal_places)); } c++ convert a fractional part of a number into integer

Ответ 3



double nmb; // инициализиация обьекта `nmb` std::string s = to_string(nmb); // удалим последные нули size_t pos = s.find_last_not_of('0'); s.erase(pos +1); // вывод дробной части std::cout << stoi( s.substr(s.find('.') + 1)); или просто: string s; cin >> s; std::cout << stoi( s.substr(s.find('.') + 1)); если нужны все цифры, а не только те, который выдает double по умолчанию Дополнение по просьбе автора: cout << (nmb - (int)nmb) << endl; тут мысль у вас правильная, но вы просто выводите значение, а nmb остается с прежним значением, так как выражение что вы выводите хранится в другой временной переменной(обьекте), которая (ый) уничтожается после вывода. Поэтому результат нужно хранить в каком то обьекте. И, так, как первоначальное значение nmb вам больше не нужно, то можете хранить в нем, чтобы не обьявить другой обьект. Теперь про самую главную ошибку: while (nmb - (int)nmb != 0)... Как я описал выше, nmb не изменил свое значение, и nmb - (int)nmb никогда не будет ноль, тем более, что вы в цикле умножаете еще на 10. Или это значение будет всегда ноль, если дробная часть nmb нулевая. Я напишу, на основе вашего кода, как будет правильно: double nmb; cin >> nmb; // получаем дробную часть nmb -= (int)nmb; // теперь nmb изменил свое значение(убрали целую часть // хотя я тут еще не учел, что значение nmb может быть отрицательным cout << nmb << endl; // Само число равно int k = nmb; while (!k || k%10 ) { nmb *= 10; k = nmb; } k = nmb/10; while (!k || k % 10 ) равносильно выражению while (k == 0 || k % 10 != 0) условие k == 0 заставляет умножать на 10 пока nmb не имеет целой части условие k%10 != 0 заставляет дальше выполнить операцию, пока число не делится на 10, т.е. пока не дойдем до последных нулей. И, так как, цикл прекращается после того, как число имеет ноль в конце, то для получения результата, нужно значение делить на 10 следовательо выводим: cout << "Result number is " << k << endl;

Ответ 4



https://ideone.com/Re3Wjx #include #include double a[] = { 1, 12.25, 22.67867897 }; char buf[256]; int main() { for (double x : a) { for (char *p=buf+sprintf(buf, "%.250f", fmod(x, 1)); *--p=='0'; *p=0); if (!buf[2]) buf[2]='0'; printf("%s\n", buf+2); } return 0; }

воскресенье, 8 декабря 2019 г.

C++: не хватает точности вычислений?

#cpp #математика #double #погрешность #точность


У меня стояла следующая задача:

Есть точки, полученные с эксперимента, они описываются некоторой теоретической гладкой
функцией f(x), которая описывается несколькими параметрами f(x, p1, p2, ..., pn)

Стоит задача подобрать параметры p, так, чтобы теоретическая функция наиболее точно
описывала экспериментальные данные (использовать метод наименьших квадратов) - другими
словами чтобы сумма квадрата разницы между теоретическими и экспериментальными значениями
была минимальной

sum((f(x) - data(x))^2) -> min


Решение

Поскольку теоретическая функция очень сложная (сумма двух логнормальных распределений,
5(6) параметров)

const long double f1 = a1 * exp(-(log(x) - mu1) * (log(x) - mu1) / (2.0 * sigma1
* sigma1)) / (x * sigma1 * 2.506628274631000502415765284811);

const long double f2 = a2 * exp(-(log(x) - mu2) * (log(x) - mu2) / (2.0 * sigma2
* sigma2)) / (x * sigma2 * 2.506628274631000502415765284811);

const long double f = f1 + f2


То к дополнительному перебору параметров от 'min' до 'max' я добавил оптимизации,
позволяющие существенно ускорить код.

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

Проблема

Все было бы замечательно, но решив посмотреть описанное выше поведение увидел следующее:



т.е. вначале плавное снижение (приближение к теоретическому), а потом какая-то фигня.

0,13186800  0,000119252701172278
0,13189500  0,000119239008348983
0,13192200  0,000119242281293030
0,13194900  0,000119245605642282
0,13197600  0,000119248981396740
0,13200300  0,000119252408556403
0,13203000  0,000119255887121271
0,13205700  0,000119241658719692
0,13208400  0,000119245240094971
0,13211100  0,000119248872875455
0,13213800  0,000119252557061144
0,13216500  0,000119256292652038
0,13219200  0,000119241551855830
0,13221900  0,000119245390257135


Подскажите с чем это может быть связано и как с этим бороться?
Мне кажется, что точности рассчётов уже не хватает просто, хотя используется 'long
double'

P.S.

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





P.P.S.

Ну или предположение о гладком поведении при изменении одного параметра является
ошибочным :( но вроде нет

P.P.P.S.

Кто хочет посмотреть код:

Исходник + файл, содержащий данные

https://yadi.sk/i/wtYtXss83YrUJc исходник (cpp)

https://yadi.sk/d/dvEnweGL3YrUFR данные

// ols.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include 
#include 
#include 
#include 
#include 
#include 

#include 

std::vector splitString(_In_ const std::string& strData, _In_ const
std::string& strSubString)
{
    std::vector lResult;
    size_t szOffset = std::string::npos;
    std::string strCurrentSlice = strData;

    while ((szOffset = strCurrentSlice.find(strSubString)) != std::string::npos)
    {
        lResult.push_back(strCurrentSlice.substr(0, szOffset));
        strCurrentSlice = strCurrentSlice.substr(szOffset + strSubString.length());
    }

    lResult.push_back(strCurrentSlice);

    return lResult;
}

int main()
{
    struct point_t
    {
        long double x;
        long double x_ln;
        long double y;
    };

    // считать файл
    std::ifstream data(L"d:\\science\\data.dat");

    std::string line;

    point_t* pointsData = new point_t[10000];

    int pointsAmount = 0;

    while (std::getline(data, line))
    {
        // распарсить строку
        std::vector parts = splitString(line, ";");

        pointsData[pointsAmount].x      = (long double)stod(parts[0]);
        pointsData[pointsAmount].x_ln   = (pointsData[pointsAmount].x == 0.0) ? 0
: log(pointsData[pointsAmount].x);
        pointsData[pointsAmount].y      = (long double)stod(parts[1]);

        pointsAmount++;
    }

    data.close();

    // подобрать коэффициенты
    struct CParameter
    {
        typedef std::pair start_point_pr;

        long double min;
        long double max;
        long double delta;
        int         steps_amount;

        CParameter(const long double in_min, const long double in_max, const int
in_steps_amount)
        {
            min = in_min;
            max = in_max;
            steps_amount = in_steps_amount;

            delta = (max - min) / steps_amount;
        }

        CParameter(const start_point_pr& in_ave, const int in_steps_amount)
        {
            min = in_ave.first * (1.0 - in_ave.second);
            max = in_ave.first * (1.0 + in_ave.second);
            steps_amount = in_steps_amount;

            delta = (max - min) / steps_amount;
        }

        CParameter(const long double in_ave, const int in_steps_amount)
            : CParameter(start_point_pr(in_ave, 0.3), in_steps_amount)
        {}

        void compact(const long double current)
        {
            min = current - delta * 2.5;
            max = current + delta * 2.5;

            delta = (max - min) / steps_amount;
        }
    };

    int         approximationMax = 5;

    const int   xIndexMin = 1;
    const int   xIndexMax = 200;// pointsAmount;

    CParameter  _sigma1(0.61991875, 50);
    CParameter  _mu1(3.72709594, 50);
    CParameter  _a1(0.86259259, 15);

    CParameter  _sigma2(0.09229050, 50);
    CParameter  _mu2(4.54333653, 50);
    CParameter  _a2(0.13740741, 15);

    long double olsSigma1 = 0.0;
    long double olsMu1 = 0.0;
    long double olsA1 = 0.0;

    long double olsSigma2 = 0.0;
    long double olsMu2 = 0.0;
    long double olsA2 = 0.0;

    __int64 maxCounter = (__int64)_sigma1.steps_amount * (__int64)_mu1.steps_amount
* (__int64)_a1.steps_amount * 
                         (__int64)_sigma2.steps_amount * (__int64)_mu2.steps_amount
/** (__int64)_a2.steps_amount*/ *
                         (__int64)approximationMax;
    __int64 localCounter = 0;

    long double olsMinCoeff = 1e20;

    for (int approximationIndex = 0; approximationIndex < approximationMax; approximationIndex++)
    {
        // перебрать параметры
        for (long double sigma1 = _sigma1.min; sigma1 <= _sigma1.max; sigma1 += _sigma1.delta)
        {
            const long double s1_1 = -1.0 / (2.0 * sigma1 * sigma1);
            const long double s1_2 = sigma1 * 2.506628274631000502415765284811;

            for (long double mu1 = _mu1.min; mu1 <= _mu1.max; mu1 += _mu1.delta)
            {
                for (long double a1 = _a1.min; a1 <= _a1.max; a1 += _a1.delta)
                {
                    const long double a2 = 1.0 - a1;

                    for (long double sigma2 = _sigma2.min; sigma2 <= _sigma2.max;
sigma2 += _sigma2.delta)
                    {
                        const long double s2_1 = -1.0 / (2.0 * sigma2 * sigma2);
                        const long double s2_2 = sigma2 * 2.506628274631000502415765284811;

                        for (long double mu2 = _mu2.min; mu2 <= _mu2.max; mu2 +=
_mu2.delta)
                        {
//                            for (long double a2 = _a2.min; a2 <= _a2.max; a2 +=
_a2.delta)
//                            {
                                // отобразить информацию
                                localCounter++;

                                if ((localCounter % 100000) == 0)
                                {
                                    const long double process = 100.0 * (long double)localCounter
/ (long double)maxCounter;

                                    std::cout << std::fixed << std::setprecision(3)
<< process << "% (" << approximationIndex << ") | ols: " << std::setprecision(8) <<
(olsMinCoeff / 1000000.0) <<
                                        " | sigma1: " << olsSigma1 << " mu1: " <<
olsMu1 << " a1: " << olsA1 <<
                                        " | sigma2: " << olsSigma2 << " mu2: " <<
olsMu2 << " a2: " << olsA2 << "      \r";
                                }

                                // вычислить МНК коэффициент
                                long double olsCoeff = 0.0;

                                for (int xIndex = xIndexMin; xIndex < xIndexMax;
xIndex++)
                                {
                                    const long double x     = pointsData[xIndex].x;
                                    const long double x_ln  = pointsData[xIndex].x_ln;
                                    const long double y     = pointsData[xIndex].y
* 1000.0;

//                                    const long double formula1 = a1 * exp(-(x_ln
- mu1) * (x_ln - mu1) / (2.0 * sigma1 * sigma1)) / (x * sigma1 * 2.506628274631000502415765284811);
//                                    const long double formula2 = a2 * exp(-(x_ln
- mu2) * (x_ln - mu2) / (2.0 * sigma2 * sigma2)) / (x * sigma2 * 2.506628274631000502415765284811);

                                    const long double formula1 = a1 * exp((x_ln -
mu1) * (x_ln - mu1) * s1_1) / (x * s1_2);
                                    const long double formula2 = a2 * exp((x_ln -
mu2) * (x_ln - mu2) * s2_1) / (x * s2_2);

                                    const long double formula = (formula1 + formula2)
* 1000.0;

                                    olsCoeff += (formula - y) * (formula - y);

                                    // если вычисленный МНК коэффициент превысил
максимальный МНК коэффициент, прекратить дальнейший анализ точек для заданной совокупности
параметров
                                    if (olsCoeff > olsMinCoeff)
                                        break;
                                }

                                // рассчитать коэффициент
                                if (olsCoeff < olsMinCoeff)
                                {
                                    olsMinCoeff = olsCoeff;

                                    olsA1 = a1;
                                    olsMu1 = mu1;
                                    olsSigma1 = sigma1;

                                    olsA2 = a2;
                                    olsMu2 = mu2;
                                    olsSigma2 = sigma2;
                                }
//                            }
                        }
                    }
                }
            }
        }

        // скорректировать (сузить) диапазон, в котором могут меняться коэффициенты
        _sigma1.compact(olsSigma1);
        _mu1.compact(olsMu1);
        _a1.compact(olsA1);

        _sigma2.compact(olsSigma2);
        _mu2.compact(olsMu2);
        _a2.compact(olsA2);
    }

    std::cout << std::endl << std::endl;
    std::cout << std::setprecision(8) << "sigma1: " << olsSigma1 << std::endl;
    std::cout << std::setprecision(8) << "mu1: " << olsMu1 << std::endl;
    std::cout << std::setprecision(8) << "a1: " << olsA1 << std::endl;
    std::cout << std::setprecision(8) << "sigma2: " << olsSigma2 << std::endl;
    std::cout << std::setprecision(8) << "mu2: " << olsMu2 << std::endl;
    std::cout << std::setprecision(8) << "a2: " << olsA2 << std::endl;
    std::cout << std::setprecision(16) << std::endl << "ols: " << olsMinCoeff <<
std::endl;

    std::cout << "Press any key to continue" << std::endl;
    _getch();

    return 0;
}

    


Ответы

Ответ 1



Harry подкинул идею, что дело действительно может быть в проседании точности при многократных вычислениях. Поэтому я при вычислении f = f1 + f1 произвёл умножение на 1000. В результате получилось следующее: График стал гладким и это хорошо! Но вот имеет несколько минимумов, хотя рассчитывал на монотонно убывающую и возрастающую :( В связи с этим наверное метод описанный в вопросе работать не будет P.S. Все, можно сказать что со второй частью вопроса разобрался :( Конечно нельзя ожидать монотонного поведения функции и я наблюдал такое только потому, что смотрел за одним (линейным) коэффициентом, а для произвольного коэффициента это не так: Например: f = a1*exp(-(ln(x)-m1)^2/(2s1^2))/(x*s1) + a2*exp(-(ln(x)-m2)^2/(2s2^2))/(x*s2) при свободном коэффициента a1 может рассматриваться как f(a1) = a1 * c1 + c2 (где c1 и c2 - константы) и тут всё просто при свободном коэффициенте 's1' все заметно сложнее (считаем производную) P.P.S. Пришла в голову такая оптимизация - поскольку график все таки гладкий, можно опираться на его поведение и пропускать некоторые точки для анализа, например: если коэффициент начал расти, не рассматривать следующую точку, а рассчитывать сразу следующую за ней (а можно установить и больший шаг).

суббота, 7 декабря 2019 г.

Десятичные дроби типа double в java [дубликат]

#java #типы_данных #double


        
             
                
                    
                        
                            This question already has answers here:
                            
                        
                    
                
                        
                            Вычисления на числах с плавающей точкой не работают
                                
                                    (2 ответа)
                                
                        
                                Closed 4 года назад.
            
                    
Есть код:

public class Drob {
    public static void main(String[] args) {
        double x = 0.1;
        System.out.println(x+x);
        System.out.println(x+x+x);      // Три раза прибавили X
        System.out.println(x+x+x+x+x);  // Пять раз прибавили X
        System.out.println(x+x+x+x+x+x+x+x+x+x); //Десять раз прибавили X
    }
}


На выходе он даёт это:

0.2
0.30000000000000004
0.5
0.9999999999999999


Против первого и третьего ответа у меня возражений нет. Но откуда на хвосте 4*(10^-17)
во втором ответе? И как это соотносится с -1*(10^-16) в четвертом ответе? 
    


Ответы

Ответ 1



Давайте зайдём с другой стороны. По стандарту IEEE 754*, число типа double хранится в виде ± n × 2k. Это значит, что при записи double d = 0.1; число в переменной d вовсе не будет равно 0.1, потому что 0.1 не представимо в указанном виде. Ясно, почему?** Вместо этого в d будет записано какое-то приближение к 0.1. Поэтому и d + d + (10 раз) + d не будет в точности равно 1. Дополнительное чтение по теме: OMG Ponies!!! *По стандарту языка, числа с плавающей запятой соответствуют стандарту IEEE 754. (И не только в Java, кстати.) **Потому что все числа упомянутого в стандарте IEEE 754 вида представляются в виде натуральной дроби, знаменатель которой есть степень двойки, а 1/10 таким числом не является. Действительно, если m/2n = 1/10, то 10 × m = 2n, и левая часть делится на 5, а правая нет.

Ответ 2



Дело в том, что данные в компьютере хранятся в бинарной системе исчесления, а не десятичной. Поэтому, если вычисления критичные, например работа с деньгами, double или float использовать нельзя. Для работы с дробями используйте класс BigDecimal . Или округляйте, если большая точность не важна. Кстати, это не связано с языком программирования, а скорее с архитектурой процессора и памяти :)

четверг, 5 декабря 2019 г.

Существует ли компилятор с языка C++, который корректно переводит десятичное число в формат IEEE-754?

#cpp #компиляция #float #double


По роду деятельности я был вынужден плотно работать с форматом IEEE-754, в процессе
чего обнаружил, что ни один из известных мне компиляторов (VC++, Intel C++, CLang,
GCC (именно MinGW)) не умеет конвертировать некоторые числа в десятичной записи (типа,
1.2345e67) в формат с плавающей точкой. Все компиляторы, что я проверял, падают на
несложных тестах, когда неочевидным для компилятора является способ округления (вверх
или вниз). Примеры таких тестов можно увидеть в этом cpp файле (код слишком длинный,
чтобы постить сюда). Мой вопрос таков: знаете ли вы компилятор, который без ошибок
пройдёт хотя бы эти простые тесты (все из этого файла)? Если таковой найдётся, я предоставлю
более сложные тесты. Просто скомпилируйте и запустите программу, она выдаст на консоль,
что пройдено, а что нет. Кто найдёт такой компилятор, прошу назвать его в ответе.

Может быть кто-то захочет проверить работу тестов на других языках, буду рад узнать
результат, только вам сначала придётся написать похожую программу на основе данных тестов.

PS. Если же кому-то будет интересна причина падения, я объяснял её в одной из своих
бесед (см. видео)

UPD: Тесты против СLang (GCC их проходит). Предложенные тесты соответствуют стандарту
языка C++ (длина строки и лексемы не превышает 65535 символов). У меня ещё есть несколько
десятков тестов против CLang, которые рушат компилятор напрочь. Все связаны с long
double (например, подайте ему на вход минимальное денормализованное число, он упадёт).
Должен, однако, признать, что для float и double CLang действительно проходит все мои
тесты, но часто выдаёт предупреждения при компиляции там, где не должен. В любом случае,
данный компилятор также не поддерживает формат IEEE-754 полностью.

UPD2: Тесты, которые валят CLang и не работают в GCC (MinGW).

UPD3: Все тесты теперь на GitHub. Позже, когда у меня наступит ясность (или, быть
может, кто-то найдёт у меня ошибки) по данному вопросу, я объединю всё в один файл
для удобства.
    


Ответы

Ответ 1



Base Gcc 5.3.0, судя по всему прошёл: $ gcc TestIEEE754Convertion.cpp TestIEEE754Convertion.cpp:80:3: warning: floating constant truncated to zero [-Woverflow] -2.470328229206232720882843964341106861825e-324, ^ TestIEEE754Convertion.cpp:19:3: warning: anonymous type with no linkage used to declare variable ‘ f32’ with linkage } f32; ^ TestIEEE754Convertion.cpp:25:3: warning: anonymous type with no linkage used to declare variable ‘ f64’ with linkage } f64; ^ $ ./a.out ::: Single precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Right on test 9 Right on test 10 Right on test 11 Right on test 12 Right on test 13 Right on test 14 Right on test 15 Right on test 16 Right on test 17 Right on test 18 Right on test 19 Right on test 20 Right on test 21 Right on test 22 Right on test 23 Gcc 4.8.5: $ ./a.out ::: Single precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Right on test 9 Right on test 10 Error on test 11: 0x7fefffffffffffff != 0x7feffffffffffffe Error on test 12: 0x7fefffffffffffff != 0x7feffffffffffffe Right on test 13 Right on test 14 Right on test 15 Right on test 16 Right on test 17 Right on test 18 Right on test 19 Error on test 20: 0x8000000000000000 != 0x8000000000000001 Right on test 21 Right on test 22 Right on test 23 Gcc 4.9.3: $ ./a.out ::: Single precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Right on test 9 Right on test 10 Right on test 11 Right on test 12 Right on test 13 Right on test 14 Right on test 15 Right on test 16 Right on test 17 Right on test 18 Right on test 19 Right on test 20 Right on test 21 Right on test 22 Right on test 23 Hard 2 Для UPD2 Gcc 5 тоже молодец: $ gcc TestIEEE754ConvertionHard2.cpp $ ./a.out OK Gcc 4.8.5 - ERROR, Gcc 4.9.3 - OK Hard 3 $ g++-5 -O0 ./TestIEEE754ConvertionHard3.cpp ./TestIEEE754ConvertionHard3.cpp:19:3: предупреждение: anonymous type with no linkage used to declare variable « f32» with linkage } f32; ^ ./TestIEEE754ConvertionHard3.cpp:25:3: предупреждение: anonymous type with no linkage used to declare variable « f64» with linkage } f64; ^ $ ./a.out ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Судя по всему прошёл. Gcc 4.9 - аналогично. Gcc 4.8 сфейлил тесты 5, 7, 8 Strict aliasing Кстати, я могу что-то путать, но разве такие преобразования: #define TEST(HI, LO, F) f = F; if (*(((unsigned short int*)&f)+4) != HI || *(unsigned long long*)&f != LO) { printf ("ERROR"); return 1; } не нарушают правило strict-aliasing? Не, конечно -Wstrict-aliasing -fstrict-aliasing это место пропускает и код на рабочих компиляторах работает. Но как-то, малость, не по себе.

Ответ 2



C# 6, судя по всему, прошёл. Адаптированный тест, MSVC 2015, выдаёт: ::: Single precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 ::: Double precision ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Right on test 9 Right on test 10 Right on test 11 Right on test 12 Right on test 13 Right on test 14 Right on test 15 Right on test 16 Right on test 17 Right on test 18 Right on test 19 Right on test 20 Right on test 21 Right on test 22 Right on test 23 ::: Double precision (TestIEEE754ConvertionHard3.cpp) ::: Right on test 0 Right on test 1 Right on test 2 Right on test 3 Right on test 4 Right on test 5 Right on test 6 Right on test 7 Right on test 8 Адаптированный исходник: class Program { // Тестирование числа одинарной точности void test32(float a, uint right, int testNumber) { var floatBytes = BitConverter.GetBytes(a); var uintValue = BitConverter.ToUInt32(floatBytes, 0); if (uintValue != right) Console.WriteLine($"Error on test {testNumber}: {uintValue:x8} != {right:x8}"); else Console.WriteLine($"Right on test {testNumber}"); } // Тестирование числа двойной точности void test64(double a, ulong right, int testNumber) { var ulongValue = (ulong)BitConverter.DoubleToInt64Bits(a); if (ulongValue != right) Console.WriteLine($"Error on test {testNumber}: {ulongValue:x16} != {right:x16}"); else Console.WriteLine($"Right on test {testNumber}"); } // Список чисел одинарной точности и правильных двоичных представлений этих чисел const int N32 = 8; float[] Test32 = new float[ N32 ] { 21267649200209254194690314461188718593.0f, 1.175494280757364291727882991035766513322e-38f, 1.175494280757364291727882991035766513323e-38f, 10749727.50000000000000000000000000000001f, 10749728.50000000000000000000000000000001f, -1.175494420887210724209590083408724842314e-38f, -1.175494420887210724209590083408724842315e-38f, -340282336497324057985868971510891282432.1f }; uint[] Right32 = new uint[ N32 ] { 0x7d800001, 0x007fffff, 0x00800000, 0x4b240720, 0x4b240721, 0x80800000, 0x80800001, 0xff7fffff }; // Список чисел двойной точности и правильных двоичных представлений этих чисел const int N64 = 24; double[] Test64 = new double[ N64 ] { -2.470328229206232720882843964341106861825e-324, -2.470328229206232720882843964341106861826e-324, 2.225073858507200641991763955462587799366e-308, 2.225073858507200641991763955462587799367e-308, 6755399441055826.499999999999999999999999, 6755399441055826.500000000000000000000001, 179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218944.1, 11417981541647677146990387413251858846007164927.9, 11417981541647677146990387413251858846007164928.0, 11417981541647677146990387413251858846007164928.1, 11417981541647677780815687527366559594358767616.0, 179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218943.9, 179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218944.0, 179769313486231560835325876058105298516207002341652166261661174625869553267292326574530099287946549246750631490335877017522087105926987962906277604735569213290190919152394180476217125334960946356387261286640198029037799514183602981511756283727771403830521483963923935633133642802139091669457927874464075218944.1, 9214843084008499.0, 0.500000000000000166533453693773481063544750213623046875, 30078505129381147446200.0, 3.518437208883201171875e13, 8.10109172351e-10, 9007199254740991.4999999999999999999999999999999995, -2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125001e-324, 6755399441055827.499999999999999999999999, 6755399441055827.5, 6755399441055827.500000000000000000000001 }; ulong[] Right64 = new ulong[ N64 ] { 0x8000000000000000, 0x8000000000000001, 0x000ffffffffffffe, 0x000fffffffffffff, 0x4338000000000052, 0x4338000000000053, 0x7fefffffffffffff, 0x497ffffffffffffe, 0x497ffffffffffffe, 0x497fffffffffffff, 0x497fffffffffffff, 0x7feffffffffffffe, 0x7feffffffffffffe, 0x7fefffffffffffff, 0x43405e6cec57761a, 0x3fe0000000000002, 0x44997a3c7271b021, 0x42c0000000000002, 0x3e0bd5cbaef0fd0c, 0x433fffffffffffff, 0x8000000000000001, 0x4338000000000053, 0x4338000000000054, 0x4338000000000054 }; // Более сложный тест из https://github.com/Zealint/fp_tests/blob/master/TestIEEE754ConvertionHard3.cpp const int N64Hard = 9; double[] Test64Hard = new double[ N64Hard ] { 2.225073858507201383090232717332404064219215980462331830553327416887204434813918195854283159012511020564067339731035811005152434161553460108856012385377718821130777993532002330479610147442583636071921565046942503734208375250806650616658158948720491179968591639648500635908770118304874799780887753749949451580451605050915399856582470818645113537935804992115981085766051992433352114352390148795699609591288891602992641511063466313393663477586513029371762047325631781485664350872122828637642044846811407613911477062801689853244110024161447421618567166150540154285084716752901903161322778896729707373123334086988983175067838846926092773977972858659654941091369095406136467568702398678315290680984617210924625396728515625e-308, 2.2250738585072018771558785585789482407880088486837041956131300312119688603996006965297904292212628858639037013670281908017171296072711910355127227413175152199055740043138804567803233377539881639177387328959246074229270113078053813397081653361296447449529789521218979090783852583365901851789618799885150427514782636076021680436220311292700454832073964845713103912225963935608322440623896907276890186717054549275173986589324810401738228328251245795065655738191038008646911615828719989708647293221449796971546706720399791990809160347625980385995424739847678861180095072511543762389603716215171729816011544604359531284325406441938645324905389137795680915804792405099227413854274942620542640408839836919187418172987793340279242767544565229087538682506419718265533447265625e-308, 2.225073858507202371221524399825492417356801716905076560672932645536733285985283197205297699430014751163740063003020570598281825052988921962169433097257311618680370015095758583081036528065392691763555900744906711111645647364804112062758171723538798309937366264595295182248000398368305570577036006227080633922504922164288936230661591439894977428478987977026639696679140794688312373772389232659678427752122018252042155806801495766953982188063736129641369100312575820243717972293621169304087413797478551780397864281278268544917722045363748655580517781818995617950934297749406849316597964346304638590078974833882923081797242441461636291003104968899481242069589385613709015202152589845793237400783350172912858237869043043055848553508913045817507736501283943653106689453125e-308, 2.22507385850720163012305563795567615250361241457301801308322872404958664760675944619203679411688695321398552054903200090343478188441232557218436756334761702051817599892294139362996674259828589999483014897143355557856769327930601597818316214242506796246078529588519927249357768832073249247992481686923224716596493432925878395010225097395757951057160073834364573849432419299709217920738991976169431413149717326525502008499797367678374315520581880443916381057236779117517775622749741380425338708447819365553307386742083452616251302946202273010905482006765402020154711200202813970014157525912344017736224427371246815175018974555997865323425588621961151633592416795802960447706494647018477736093430045142168360701364747951396213837722826145437693412532098591327667236328125e-308, 2.22507385850720163012305563795567615250361241457301801308322872404958664760675944619203679411688695321398552054903200090343478188441232557218436756334761702051817599892294139362996674259828589999483014897143355557856769327930601597818316214242506796246078529588519927249357768832073249247992481686923224716596493432925878395010225097395757951057160073834364573849432419299709217920738991976169431413149717326525502008499797367678374315520581880443916381057236779117517775622749741380425338708447819365553307386742083452616251302946202273010905482006765402020154711200202813970014157525912344017736224427371246815175018974555997865323425588621961151633592416795802960447706494647018477736093430045142168360701364747951396213837722826145437693412532098591327667236328124999e-308, 2.22507385850720163012305563795567615250361241457301801308322872404958664760675944619203679411688695321398552054903200090343478188441232557218436756334761702051817599892294139362996674259828589999483014897143355557856769327930601597818316214242506796246078529588519927249357768832073249247992481686923224716596493432925878395010225097395757951057160073834364573849432419299709217920738991976169431413149717326525502008499797367678374315520581880443916381057236779117517775622749741380425338708447819365553307386742083452616251302946202273010905482006765402020154711200202813970014157525912344017736224427371246815175018974555997865323425588621961151633592416795802960447706494647018477736093430045142168360701364747951396213837722826145437693412532098591327667236328125001e-308, 2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984374999e-308, 2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375e-308, 2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375001e-308 }; ulong[] Right64Hard = new ulong[ N64Hard ] { 0x0010000000000000, 0x0010000000000001, 0x0010000000000002, 0x0010000000000000, 0x0010000000000000, 0x0010000000000001, 0x0010000000000001, 0x0010000000000002, 0x0010000000000002 }; static void Main(string[] args) { new Program().Run(); } void Run() { // Проверка всех чисел одинарной точности в цикле Console.WriteLine("\n\n::: Single precision :::\n"); for (int i = 0; i < N32; i++) test32(Test32[i], Right32[i], i); // Проверка всех чисел двойной точности в цикле Console.WriteLine("\n::: Double precision :::\n"); for (int i = 0; i < N64; i++) test64(Test64[i], Right64[i], i); // Более сложная проверка (TestIEEE754ConvertionHard3.cpp) Console.WriteLine("\n::: Double precision (TestIEEE754ConvertionHard3.cpp) :::\n"); for (int i = 0; i < N64Hard; i++) test64(Test64Hard[i], Right64Hard[i], i); } } Обновление: тесты с long double перенести на C# не получится, т. к. аналога типа данных long double (80 бит с плавающей запятой) в .NET текущей версии нету. Обновление: добавил тесты из https://github.com/Zealint/fp_tests/blob/master/TestIEEE754ConvertionHard3.cpp

Ответ 3



gcc 4.9.2 вроде проходит. На всех четырёх. Везде "Right on" и "OK".

среда, 27 ноября 2019 г.

Вывод числа double (10^18)+1

#double #c #вывод #c++


Увидев этот код
#include 
#include 
using namespace std;
int main()
{
    double d = 1000000000000000001;
    cout.setf(ios::fixed);
    cout.precision(0); //0 - число символов после точки
    cout << d << endl;
    printf("%.0lf\n",d);
    return 0;
}

Неожиданно увидел в результате (http://ideone.com/1SEHpO)

1000000000000000000
1000000000000000000


Куда делась 1?
Из за чего так происходит? Особенности записи типов с плавающей точкой?    


Ответы

Ответ 1



У типа double ограниченная точность, поэтому в нём невозможно различить 1000000000000000000 и 1000000000000000001. Если вы объявите double d1 = 1000000000000000001; double d2 = 1000000000000000000; — то d1 и d2 будут одним и тем же числом. Пруф: http://ideone.com/WqQSAd Проблема в том, что числа с плавающей точкой не бесконечно точны. Для типа double, например, выделяется 52 бита под значащие цифры (и ещё 11 бит под показатель степени), число внутри хранится как бы закодированным в виде CCCCCCC * 2^PPPP (C — значащие цифры, P — степень). А значит, числа, которые можно представить в виде double, расположены с некоторым шагом (который зависит от величины порядка): числа, расположенные «между» представимыми числами выразить точно с помощью double вообще нельзя, и они автоматически округляются до ближайшего представимого числа. Пример такого числа — 0.1: оно не выражается (двоичной) дробью, и константа 0.1 внутри хранится как приблизительно 0.1000000000000000055511151231257827021181583404541015625 Максимальное значение, которое можно прибавить к единице так, чтобы она не изменилась, называется машинным эпсилоном. Для типа double машинный эпсилон равен, очевидно, 2⁻⁵³, то есть около 1.11e-16. Почему очевидно? Потому что для единицы значащие биты такие: 10000000...000, а значит, следующее по величине число, которое можно выразить типом double, должно иметь значащие цифры 10000000...001. (На самом деле чуть-чуть сложнее: ведущая единица не хранится, а подразумевается.) Отсюда выплывает, что 1 + 1e-16 для типа double неотличимо от 1. Поскольку для бóльших чисел увеличивается порядок, при увеличении первого слагаемого машинный эпсилон приблизительно пропорционально увеличивается. Соответственно, 1e16 + 1 будет равно 1e16. В вашем же случае вы на два порядка выше предела: вы прибавляете к 1e18. Давайте ещё проэкспериментируем: http://ideone.com/4XJxFj 1 + 1.110223024625156e-16 == 1 но уже 1 + 1.110223024625157e-16 != 1 Это потому, что 1.110223024625156e-16 — приближение к 2⁻⁵³. Ещё немного информации о числах с плавающей точкой: Хорошая статья на Хабре Классическая статья с теоретическими выкладками, но на английском. Если вам нужно представлять числа с высокой точностью, даже типа long double может не хватить. В этом случае, возможно, придётся применять числа бесконечной точности. Такие числа являются встроенными в некоторых языках (например, Java и C#), а для C и C++ есть хорошие библиотеки, предоставляющие такие числа. Я бы порекомендовал GMP.

понедельник, 25 ноября 2019 г.

В чем хранить “деньги”? Float / Double


Был на собеседовании, спросили, "в переменной какого типа лучше хранить деньги"
сказал Float, мол скорость, все дела. - забраковали. Ответа на вопрос "почему" не услышал. так вот почему "деньги" нельзя хранить во Float? 
    


Ответы

Ответ 1



Для денег важны "копейки". Потеря любой значащей цифры в финансовой сфере недопустима Поскольку числа хранятся в двоичной системе - почти любое десятичное нецелое число не имеет конечное количество цифр после запятой. Поскольку мы не можем хранить бесконечно большое количество цифр после запятой, часть числа теряется. Простой пример: 5.1 переведем в двоичный вид Целая часть имеет только 3 цифры 5₁₀ = 1*2² + 0*2¹ + 1*2⁰ = 101₂ А вот дробная... .1₁₀ = 0*2⁻¹ + 0*2⁻² + 0*2⁻³ + 1*2⁻⁴ + 1*2⁻⁵ + 0*2⁻⁶ + 0*2⁻⁷ + 1*2⁻⁸ + 1*2⁻⁹... Если взять только первые 7 цифр (двоичных) после запятой, получится не 0.1, а 0.09375 Добавлено: Что касается подходящих типов (поскольку на вопросе тег Java - пример для него) для хранения финансовых данных, как уже написали в комментариях и соседних ответах есть два подхода: Типы с произвольной точностью (например BigDecimal). В исходники не смотрел но внутри хранение скорее всего происходит или в строках или массивах цифр; Целочисленные примитивные типы (int или long). При этом в переменных финансы хранятс в неделимых единицах измерения (это не всегда копейки/центы, в ряде случаев должны учитываться, например, сотые доли этих единиц) Как всегда выбор должен определяться спецификой задачи. Типы с произвольной точность скорее всего будут обрабатываться медленнее чем примитивы, но в случае с примитивами нужно "помнить" что мы в них храним, чтобы при выгрузке в смежные системы не получить астрономические суммы.

Ответ 2



Немного, наверное, конкретики можно внести без углубления в тонкости вычислений машины:) Тут надо еще уточнение, какие операции будут с деньгами производиться. Для платежей и переводов - достаточен long. А сама сумма - в минорных единицах. Потом что мы не платим десятыми долями копеек/центов. Т.е. для 1 руб. 10 копеек, будем перечислять 110. Хранить также можно в таком же типе. Но уже появляются вопросы, когда надо проводить вычисления. К примеру, насчитат некий процент за месяц. Тут уж типы double/float, как заметили некоторые, могут дават погрешности из-за тонкостей стандарта чисел с плавающей запятой. Но есть хороший выход есть объекты чисел, которые хранят все значения в целых числах. Конкретнее: два целочисленных значения: мантиссу вещественного числа в виде объекта класса большого целочисленного, и неотрицательный десятичный порядок числа типа int. Тут напрашивается пример для Java: класс BigDecimal. Я бы хранил деньги в нем. Ну а если еще углубиться в расчеты, то для всяких операций есть свои стандарты. Например, если рассчитываем пеню, то округление в большую сторону: При пене в 1.123456 руб, получим 1.13. Все просто. Если кто нашел в моем ответе ошибку, пожалуйста, поправьте меня. Я только въезжаю в финансовые расчетные операции.

Ответ 3



Каждый сам решает в чем хранить деньги, но наиболее стабильной версией является использования класса BigDecimal. Преимущества Точность и значность известна Арифметики и сравнение включены Поддерживается в JDK, JDBC и т.д. Неплохая производительность