www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - float.sizeof should be 4

reply Steven Schveighoffer <schveiguy gmail.com> writes:
Not `4LU`.

For the billionth time while porting raylib to D, I'm having to 
replace `n*float.sizeof` with `n*int(float.sizeof)` because 
otherwise, it doesn't fit into the `int` I'm assigning it to.

Why isn't it just 4? 4 will convert to any width you want. It 
does not poison integer multiplications or additions.

The chances that `T.sizeof > int.max` is near zero. And even if 
it did, making it the same as a number literal would just work -- 
it would become `size_t` if it was too big.

-Steve
Sep 22
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 9/22/24 14:59, Steven Schveighoffer wrote:
 Not `4LU`.
 
 For the billionth time while porting raylib to D, I'm having to replace 
 `n*float.sizeof` with `n*int(float.sizeof)` because otherwise, it 
 doesn't fit into the `int` I'm assigning it to.
 
 Why isn't it just 4? 4 will convert to any width you want. It does not 
 poison integer multiplications or additions.
 
 The chances that `T.sizeof > int.max` is near zero. And even if it did, 
 making it the same as a number literal would just work -- it would 
 become `size_t` if it was too big.
 
 -Steve
Well, the point of contention is not that `float.sizeof > int.max`. Rather, `n*float.sizeof` could be bigger than `int.max`.
Sep 22
parent reply Johan <j j.nl> writes:
On Sunday, 22 September 2024 at 14:59:36 UTC, Timon Gehr wrote:
 On 9/22/24 14:59, Steven Schveighoffer wrote:
 Not `4LU`.
 
 For the billionth time while porting raylib to D, I'm having 
 to replace `n*float.sizeof` with `n*int(float.sizeof)` because 
 otherwise, it doesn't fit into the `int` I'm assigning it to.
 
 Why isn't it just 4? 4 will convert to any width you want. It 
 does not poison integer multiplications or additions.
 
 The chances that `T.sizeof > int.max` is near zero. And even 
 if it did, making it the same as a number literal would just 
 work -- it would become `size_t` if it was too big.
 
 -Steve
Well, the point of contention is not that `float.sizeof > int.max`. Rather, `n*float.sizeof` could be bigger than `int.max`.
Is that true? `n*int(float.sizeof)` could also be bigger than `int.max`, but Steven says it works for his case. ( Steven: best to show the full case). -Johan
Sep 22
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Sunday, 22 September 2024 at 16:45:23 UTC, Johan wrote:
 On Sunday, 22 September 2024 at 14:59:36 UTC, Timon Gehr wrote:
 On 9/22/24 14:59, Steven Schveighoffer wrote:
 Not `4LU`.
 
 For the billionth time while porting raylib to D, I'm having 
 to replace `n*float.sizeof` with `n*int(float.sizeof)` 
 because otherwise, it doesn't fit into the `int` I'm 
 assigning it to.
 
 Why isn't it just 4? 4 will convert to any width you want. It 
 does not poison integer multiplications or additions.
 
 The chances that `T.sizeof > int.max` is near zero. And even 
 if it did, making it the same as a number literal would just 
 work -- it would become `size_t` if it was too big.
 
Well, the point of contention is not that `float.sizeof > int.max`. Rather, `n*float.sizeof` could be bigger than `int.max`.
This is true for any integer calculation. Though, it is true that this would be painful if you'd have to increase the precision explicitly, and a failure to do so would be a silent error. So I have to withdraw this. The correct solution is likely to just write a function to do this correctly. Ugh.
 Is that true?
 `n*int(float.sizeof)` could also be bigger than `int.max`, but 
 Steven says it works for his case.  ( Steven: best to show the 
 full case).
See all the modifications here: https://github.com/schveiguy/draylib/commit/b665f75c453afa6ab8e4f8108fe1bd296f72d53e#diff-10364ecc46979709c69d6caaeb639fedf2ffa2db80d4d61a7172b7a8a374dd12 (you have to tell github to load the diff). -Steve
Sep 22
prev sibling next sibling parent Andrea Fontana <nospam example.com> writes:
On Sunday, 22 September 2024 at 12:59:04 UTC, Steven 
Schveighoffer wrote:
 Not `4LU`.
But it represents a size, not a number. It's fine for me it is size_t.
Sep 22
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
I agree that it seems to make sense making 4 an `int`.

I remember the bad old days, when an `int` was 16 bits. The transition to 32 
bits broke an awful lot of code. Since the language does not recognize integer 
overflow, address calculations left and right would wrap around. Things like 
`malloc(n * sizeof(i))` would overflow the byte count, the malloc'd buffer
would 
be too small, and malware would be injected.

The most effective solution is to make sizes a consistent type, which is size_t.

This does not eliminate the possibility of overflows, but dramatically reduces 
its incidence.
Sep 22
parent reply Dom DiSc <dominikus scherkl.de> writes:
On Monday, 23 September 2024 at 05:34:51 UTC, Walter Bright wrote:
 I agree that it seems to make sense making 4 an `int`.
A number literal should always be of the smallest type that can represent it - it will be implicitly converted to larger types anyway. So, 4 should be ubyte (as only negative literals should be of signed type and there is no smaller type in D than ubyte - else it should be of type "nibble" (4bit)).
Sep 23
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, September 23, 2024 5:08:37 PM MDT Dom DiSc via Digitalmars-d wrote:
 On Monday, 23 September 2024 at 05:34:51 UTC, Walter Bright wrote:
 I agree that it seems to make sense making 4 an `int`.
A number literal should always be of the smallest type that can represent it - it will be implicitly converted to larger types anyway. So, 4 should be ubyte (as only negative literals should be of signed type and there is no smaller type in D than ubyte - else it should be of type "nibble" (4bit)).
4 will work in any context that requires a ubyte, because the compiler will know that it will fit. The issue is what it defaults to. In this case, it's size_t, because it has to do with memory size, and you really want stuff like T.sizeof * numberOfTs to result in size_t, because it represents the amount of memory being used, and if it's not size_t, it risks being truncated. It would be particularly bad if it were treated as ubyte given how little memory that would be. So, the issue really isn't the value itself. That will convert to other sizes just fine, particularly since it's known at compile time. Rather, what matters is what you get when you do math on it, and that really needs to be size_t, because it's used for memory sizes. - Jonathan M Davis
Sep 23
parent reply Dom DiSc <dominikus scherkl.de> writes:
On Tuesday, 24 September 2024 at 03:23:31 UTC, Jonathan M Davis 
wrote:
 So, the issue really isn't the value itself. That will convert 
 to other sizes just fine, particularly since it's known at 
 compile time. Rather, what matters is what you get when you do 
 math on it
Doing math most of the time involves at least two values, in this case sizeof (of type size_t) and 4 (a nibble). It should be clearly defined in what type such an operation should result. Best would be: the larger type of the two operands, here size_t (but for multiplication also the next larger type would be reasonable, if you want to be sure to never get an overflow). So if you do ubyte * 4 the result should be ubyte, because ubyte is the larger of the two types. It doesn't matter that the calculation is done in the size of the registers of the machine, may that be 8bit, 16bit, 32bit or 64bit (or maybe any unusual odd word size), but the result should be per default restricted to the type of the larger operand (or collected from multiple registers if the type is larger than the machine word size). If you want a different result type, you should cast at least one of the operands to the desired result type beforehand. Of course there is also math with only one value. This should result in the same type as the operand. The unitary operators should NEVER change the type of its parameter. This is an awful behaviour of D that should be burned with fire!
Sep 24
next sibling parent Dom DiSc <dominikus scherkl.de> writes:
On Tuesday, 24 September 2024 at 10:49:45 UTC, Dom DiSc wrote:
 The unitary operators should NEVER change the type of its 
 parameter. This is an awful behaviour of D that should be 
 burned with fire!
Except (of course) a cast, which is also a unitary operator :-)
Sep 24
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On Tuesday, 24 September 2024 at 10:49:45 UTC, Dom DiSc wrote:
 On Tuesday, 24 September 2024 at 03:23:31 UTC, Jonathan M Davis 
 wrote:
 So, the issue really isn't the value itself. That will convert 
 to other sizes just fine, particularly since it's known at 
 compile time. Rather, what matters is what you get when you do 
 math on it
Doing math most of the time involves at least two values, in this case sizeof (of type size_t) and 4 (a nibble).
You might want to make sure you understand the problem before commenting, that would save everybody some time. sizeof is a property here, not a value, and that property has for value 4, and type size_t.
Sep 24
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Tuesday, 24 September 2024 at 13:36:06 UTC, deadalnix wrote:
 sizeof is a property here, not a value, and that property has 
 for value 4, and type size_t.
When we want to look at the size of an enum, why is it 128 bytes if its original type is string? It's off topic but I want to give an example: ```d enum iColor : short { R, G, B } //* enum sColor : string { R = "red", G = "green", B = "blue" }//*/ template ByteSize(E) if (is(E == enum)) {    alias T = imported!"std.traits".OriginalType!E;    enum ByteSize = T.sizeof * 8; } import std.stdio; void main() {    ByteSize!iColor.writeln(" == ",            iColor.sizeof * 8);    ByteSize!sColor.writeln(" == ",            sColor.sizeof * 8);        String.sizeof.writeln; // 48 } struct String {    immutable(char)[] str;    size_t a, b, c, length; }  ``` SDB 79
Sep 24
parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 24 September 2024 at 14:06:50 UTC, Salih Dincer wrote:
 On Tuesday, 24 September 2024 at 13:36:06 UTC, deadalnix wrote:
 sizeof is a property here, not a value, and that property has 
 for value 4, and type size_t.
When we want to look at the size of an enum, why is it 128 bytes if its original type is string?
I assume you mean "bits," not "bytes." A string in D consists of a 64-bit pointer and a 64-bit length, for a total of 128 bits (or 16 bytes).
Sep 24
prev sibling next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, September 24, 2024 4:49:45 AM MDT Dom DiSc via Digitalmars-d 
wrote:
 On Tuesday, 24 September 2024 at 03:23:31 UTC, Jonathan M Davis

 wrote:
 So, the issue really isn't the value itself. That will convert
 to other sizes just fine, particularly since it's known at
 compile time. Rather, what matters is what you get when you do
 math on it
Doing math most of the time involves at least two values, in this case sizeof (of type size_t) and 4 (a nibble). It should be clearly defined in what type such an operation should result.
As long as sizeof is size_t, then math will give you ulong on 64-bit systems and uint on 32-bit systems. The main problem with it not being a size_t is if you're on a 64-bit system and you do math with a 32-bit value. For instance, it's quite common for folks to just use int by default with many numbers, but the result of multiplying such a value against a sizeof really needs to be size_t if you don't want potential overflow issues. If sizeof defaulted to ubyte just because it's a value that can fit in a ubyte, then multiplying it against an int would give you an int when what you really need if you want to avoid bugs is a ulong / size_t, because that's what's needed to represent larger memory sizes. There would quite easily be problems with large arrays if the type being used weren't size_t. And yes, having sizeof be treated as size_t does sometimes get annoying when you don't actually need the larger values, and you want to do something other than get a value to use for something other than memory manipulation, but it means that you get the correct result by default, because you're guaranteed to be dealing with the correct type for memory sizes. If you then actually need a smaller type, you can cast to a smaller type. And since sizeof typically gives a value that fits in a ubyte (and definitely does with float), and the value is known at compile-time, you can always do something like ubyte v = float.sizeof; if you want to be dealing with a ubyte. D defaults to the size that is the least likely to result in silent bugs and then allows you to convert to smaller types if you want to for your particular application. The entire reason that Steven is running into issues here is because he's deciding for whatever reason when dealing with raylib to use int for memory sizes when it's usually the case that the correct thing to do is to use size_t in order to ensure that you can correctly handle large arrays (and that can easily come up with stuff like manipulating files given how large they can get these days). - Jonathan M Davis
Sep 24
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 9/24/2024 3:49 AM, Dom DiSc wrote:
 It should be clearly defined in what type such an operation should result.
D uses the C integer promotion rules for this. It's well defined.
Sep 25
prev sibling parent kdevel <kdevel vogtner.de> writes:
On Sunday, 22 September 2024 at 12:59:04 UTC, Steven 
Schveighoffer wrote:
 The chances that `T.sizeof > int.max` is near zero.
Code with `T.sizeof > uint.max` would not even compile: https://issues.dlang.org/show_bug.cgi?id=21995 Issue 21995 - Struct with size uint.max or greater causes ICE
Sep 25