float w C vs float w C#

Spine
  • Rejestracja:około 22 lata
  • Ostatnio:2 minuty
  • Postów:6694
1

Tak sobie sprawdzałem, czy wynik float dla liczby podzielonej przez 2.0f będzie taki sam jak jak wynik float tej samej liczby pomnożonej przez 0.5f.
Najpierw wykonałem test w języku C#, ale przy wypisywaniu nie mogłem uzyskać wszystkich miejsc po przecinku!
Potem pomyślałem, że C jednak jest bardziej niskopoziomowy i w nim dam radę. No i mam ;)

C#: https://ideone.com/cmgLUY

Kopiuj
using System;

public class Test
{
	public static void Main()
	{
		float a = 7.52321311545f;
		
		Console.WriteLine(a.ToString("N30"));
		Console.WriteLine((a / 2.0f).ToString("N30"));
		Console.WriteLine((a * 0.5f).ToString("N30"));
	}
}

Wynik C#:

Kopiuj
7.523213000000000000000000000000
3.761606000000000000000000000000
3.761606000000000000000000000000

C: https://ideone.com/AJQetN

Kopiuj
#include <stdio.h>
 
int main(void) {
	float a = 7.52321311545f;
	printf("%0.30f\n%0.30f\n%0.30f\n", a, a/2.0f, a * 0.5f);
	return 0;
}

Wynik C:

Kopiuj
7.523212909698486328125000000000
3.761606454849243164062500000000
3.761606454849243164062500000000

W C# jest 6 miejsc po przecinku, a w C ok. 20 miejsc po przecinku. Dlaczego C# obcina floata, chociaż każę mu tego nie robić?


🕹️⌨️🖥️🖱️🎮
edytowany 2x, ostatnio: Spine
KA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 lata
2

By default, the return value only contains 7 digits of precision although a maximum of 9 digits is maintained internally. If the value of this instance has greater than 7 digits, ToString(String) returns PositiveInfinitySymbol or NegativeInfinitySymbol instead of the expected number. If you require more precision, specify format with the "G9" format specification, which always returns 9 digits of precision, or "R", which returns 7 digits if the number can be represented with that precision or 9 digits if the number can only be represented with maximum precision

źródło: https://docs.microsoft.com/en-us/dotnet/api/system.single.tostring?view=netframework-4.7.2#System_Single_ToString_System_String_

Wygląda na to, że więcej nie wypiszesz bo, toString zwraca max. 9 znaków a resztę wypełnia zerami.

Spine
  • Rejestracja:około 22 lata
  • Ostatnio:2 minuty
  • Postów:6694
0

Wciąż mało, ale dzięki za znalezienie wyjaśnienia ;)


🕹️⌨️🖥️🖱️🎮
KA
Taką precyzję ma float. Ale zawsze możesz rzutować na double
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 10 godzin
5
Kopiuj
       float a = 7.52321311545f;

Ta liczba ma więcej cyfr znaczących niż float jest w stanie pomieścić niezależnie od tego czy to C czy C#, więc na tym w sumie można zakończyć analizę.

Spine
  • Rejestracja:około 22 lata
  • Ostatnio:2 minuty
  • Postów:6694
0

Ale ja właśnie chciałem też dostać te nieznaczące śmieci. A ilość miejsc po przecinku w przykładowej liczbie tak sobie po prostu rosła przy testowaniu. Wpisywałem byle co, żeby zobaczyć czy *0.5 będzie różne od /2.


🕹️⌨️🖥️🖱️🎮
edytowany 1x, ostatnio: Spine
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 10 godzin
0

Ale ponad te 7 czy 9 cyfr będziesz dostawał właśnie śmieci, a ty tam oczekujesz konkretnych cyfr, czegoś czego float ci nie zapewnia.

Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:około 8 godzin
2

Spróbuj wypisywać floata szesnastkowo. Wtedy nie będzie wymówek o cyfrach znaczących :]


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
Spine
  • Rejestracja:około 22 lata
  • Ostatnio:2 minuty
  • Postów:6694
0

No i mamy wyniki.

https://ideone.com/5WauYd

Kopiuj
using System;

public class Test
{
	public static void Main()
	{
		float a = 7.5232f;
		
		Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(a)));
		Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(a / 2.0f)));
		Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(a * 0.5f)));
	}
}

https://ideone.com/G4SEEP

Kopiuj
#include <stdio.h>
#include <string.h>

int main(void) {
	float a = 7.5232f;
	unsigned int ui;
	memcpy(&ui, &a, sizeof (ui));
	printf("%x \n", ui);
	
	a = 7.5232f;
	a = a / 2.0f;
	memcpy(&ui, &a, sizeof (ui));
	printf("%x \n", ui);
	
	a = 7.5232f;
	a = a * 0.5f;
	memcpy(&ui, &a, sizeof (ui));
	printf("%x \n", ui);
	
	return 0;
}

Wynik C#:

Kopiuj
29-BE-F0-40
29-BE-70-40
29-BE-70-40

Wynik C:

Kopiuj
40f0be0e 
4070be0e 
4070be0e 

Pewnie kolejność bajtów w jednym z wyników trzeba by odwrócić, ale liczba wychodzi prawie ta sama. 29 (pierwszy bajt c#) nie pasuje do 0e (ostatni bajt C). A może to kolejny dowód automatycznego zaokrąglania w C# ? (o ile konwersja jest poprawna).


🕹️⌨️🖥️🖱️🎮
edytowany 7x, ostatnio: Spine
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 2 miesiące
0

Gdzie kompilujesz? https://dotnetfiddle.net/VQIFHV daje

Kopiuj
0E-BE-F0-40
7.5232

Za https://gregstoll.dyndns.org/~gregstoll/floattohex/ :

Kopiuj
0x40f0be0e == 7.5232

a

Kopiuj
0x40f0be29 == 7.52321

Podaj platformę, kompilator i wersję CLR.

Spine
  • Rejestracja:około 22 lata
  • Ostatnio:2 minuty
  • Postów:6694
0

Wszystko na ideone odpalałem ;)


🕹️⌨️🖥️🖱️🎮
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 2 miesiące
0

To skąd wziąłeś 29, jeżeli ten link https://ideone.com/5WauYd (podany przez Ciebie) wypisuje 0E-BE-F0-40 ?

Spine
  • Rejestracja:około 22 lata
  • Ostatnio:2 minuty
  • Postów:6694
0

Skopiowałem, nie wiem czemu teraz jest inaczej.... może to zależy od śmieci w ramie :D ?


🕹️⌨️🖥️🖱️🎮
AF
Ach to promieniowanie kosmiczne ;)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.