It is currently October 19th, 2020, 9:33 pm

Converting Bytes (AutoScale Alternative)

Tips and Tricks from the Rainmeter Community
User avatar
deflore08
Posts: 51
Joined: July 12th, 2020, 7:47 am

Re: Converting Bytes (AutoScale Alternative)

Post by deflore08 »

Oh great, i quess it will save me in a places where i cannot use autoscale. Really helpful, thank you. :)
User avatar
balala
Rainmeter Sage
Posts: 11575
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Converting Bytes (AutoScale Alternative)

Post by balala »

limitless wrote: September 22nd, 2020, 12:16 am I used this in my script, very good!!! I had an issue where I needed to set the Measure to the right size from bytes and didn't need to create a meter as I am outputting this info into a ToolTip. :thumbup:
And do you have any question related to your code? If you have, post the code please.
User avatar
Yincognito
Posts: 2760
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Converting Bytes (AutoScale Alternative)

Post by Yincognito »

ikarus1969 wrote: September 22nd, 2020, 9:51 am I want to throw in a solution provided by user smurfier in this thread/post about the recycle bin introducing the "scary math" for getting scale and the scaled size for a number of bytes.

a slightly adapted excerpt from the post:
...
Yeah, that was my starting point as well, when I set up the solution used in my (tooltip featured) skins years ago, but it has several mistakes and drawbacks:

- values between 0 and 1 give wrong results (try 0.25, for example; that's because Log() is negative for all the values greater than 0 and less than 1)
- negative values yield errors (try -15, for example; sure, it's unlikely to have to scale negative values, but still)
- values equal to the scale are not converted (try 1024, for example; it is left as 1024, when it should be 1, since 1024 is 1 kb)
- decimals don't get to the final result if they are 0 (try 1029, for example; it should be 1.00, but it is 1)
- two measures are needed in his implementation, when just one is enough (the "B" suffix can be added in the meter, just like for built-in autoscaling)
- his implementation only does fixed scaling to 1024, even though variable scaling (e.g. the equivalent of AutoScale option, to whatever number) is possible

These are clear when changing the Formula from [Measure_Bin_Size] in the code below and comparing the built-in autoscale with the scale in the tooltip:

Code: Select all

; Kilobyte (kB) = 1024
; Megabyte (MB) = 1048576 or 1024**2
; Gigabyte (GB) = 1073741824 or 1024**3
; Terabyte (TB) = 1099511627776 or 1024**4

[Variables]
Decimals=2

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1
BackgroundMode=2
SolidColor=47,47,47,255

---Measures---

[Measure_Bin_Size]
Measure=Calc
Formula=1024

[Measure_Scale]
Measure=CALC
Formula=Measure_Bin_Size = 0 ? 1 : CEIL(LOG(Measure_Bin_Size) / (10 * LOG(2)))
Substitute="1":"B","2":"kB","3":"mB","4":"gB","5":"tB","6":"pB","7":"eB","8":"zB","9":"yB"
DynamicVariables=1

[Measure_Scaled_Size]
Measure=CALC
Formula=ROUND(Measure_Bin_Size / 1024 **(Measure_Scale - 1), #Decimals#)
DynamicVariables=1

---Meters---

[Meter_Scaled_Size]
Meter=STRING
X=0
Y=0
FontFace=Consolas
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
FontSize=16
AntiAlias=1
MeasureName=Measure_Bin_Size
AutoScale=1
NumOfDecimals=#Decimals#
Text="Inbuilt Scale = %1"
TooltipText="Custom Scale = [Measure_Scaled_Size] [Measure_Scale]"
DynamicVariables=1
So, the solution I extensively use in my skins is an adapted, corrected and much more flexible one, at the expense of yet more "scary math" :lol: :

Code: Select all

[Variables]
Scale=1024
Decimals=2

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1
BackgroundMode=2
SolidColor=47,47,47,255

---Measures---

[RawNumber]
Measure=Calc
Formula=8618111

[ScaledNumber]
Measure=Calc
Formula=Sgn(RawNumber=0?1:RawNumber)*(Round(Abs(RawNumber)/#Scale#**Trunc(Log(Abs(RawNumber)<1?1:Abs(RawNumber))/Log(#Scale#)),#Decimals#)+(Trunc(Log(Abs(RawNumber)<1?1:Abs(RawNumber))/Log(#Scale#))+1)*(10**(-#Decimals#-2)))
RegExpSubstitute=1
Substitute="^(.*\..{#Decimals#}).{1}(.{1}).*$":"\1\2","1$":"  ","2$":" k","3$":" M","4$":" G","5$":" T","6$":" P","7$":" E","8$":" Z","9$":" Y"
DynamicVariables=1

---Meters---

[ScaledNumbers]
Meter=STRING
X=0
Y=0
FontFace=Consolas
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
FontSize=16
AntiAlias=1
MeasureName=RawNumber
AutoScale=1
NumOfDecimals=#Decimals#
Text="Inbuilt Scale = %1B"
TooltipText="Custom Scale = [ScaledNumber]B"
DynamicVariables=1
It's more or less the same idea, so the formula shouldn't scare anyone (I know, right?). Naturally, any numerical measure can be in place of RawNumber. Just like in the previous code above, built-in autoscale is shown as Inbuilt Scale, while the autoscaling for tooltips is shown in the tooltip text, as Custom Scale:
Custom AutoScale.jpg
Nowadays I use my custom tooltips instead of the operating system ones, but I still use this autoscale approach, even though in theory they wouldn't be absolutely needed in a custom tooltip. This was a good post to check, as I improved my approach as well (such as using Trunc() instead of Ceil() to fix the value being equal to the scale scenario above) before posting this.
You do not have the required permissions to view the files attached to this post.
User avatar
ikarus1969
Posts: 392
Joined: February 28th, 2011, 3:20 pm
Location: Vienna, Austria

Re: Converting Bytes (AutoScale Alternative)

Post by ikarus1969 »

Yincognito wrote: September 23rd, 2020, 5:09 pm So, the solution I extensively use in my skins is an adapted, corrected and much more flexible one, at the expense of yet more "scary math" :lol: :
Thanks for sharing your solution (again?)! I definitely will adapt my skins - and yes it is scary :D
User avatar
Yincognito
Posts: 2760
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Converting Bytes (AutoScale Alternative)

Post by Yincognito »

ikarus1969 wrote: September 23rd, 2020, 6:45 pm Thanks for sharing your solution (again?)! I definitely will adapt my skins - and yes it is scary :D
Yeah, the last time I shared it, it was good but still had some minor flaws, e.g. it didn't cover negative values and didn't convert the values equal to the scale (I discovered the latter while preparing to post it here, actually). That's why I said seeing this post was really positive as I perfected the solution - so it helped me as well.

Well, my regexes are scary, my formulas are scary, my code is "hideously complicated" as someone likes to say, but they all work, LOL. To me, that's the most important thing - and believe me, I would have made it less scary if I could, but math is math, it has its own rules... By the way, the formula is shorter if you don't cover the negative values, as the Sgn(...) and the Abs() parts are not needed then. Everything revolves around Log(RawNumber)/Log(#Scale#), in simplified terms, really, and adding a 0.___0N to the result in order to store the numerical codes of k, M, G, T, ... which are then converted to the actual letters in the Substitute.

In other words, if you skip the repetitive exception handling stuff, it's:
Round(RawNumber/#Scale#**Trunc(Log(RawNumber)/Log(#Scale#)),#Decimals#) <-- this is the actual scaled value
+
(Trunc(Log(RawNumber)/Log(#Scale#))+1)*(1/10**(#Decimals#+2)) <-- this is a fractional numerical suffix to store the k, M, G, ... "codes"

It becomes much easier to understand if you replace the measure and variable names with actual numbers. It's also useful to wrap your head around the concept that in the base 10 that we humans use, the (base 10) Log(x) more or less counts how many digits are either before (when Log(x) is positive) or after (when Log(x) is negative) the decimal point (aka .). So, in a way, by doing Log(RawNumber)/Log(#Scale#) we divide how many digits before the decimal point RawNumber has to how many digits before the decimal point the Scale has, in order to get how many Scale digits RawNumber has.

For example, if we want to scale 5 700 000 by a factor of 1 000 we have: Log(1 000)=3 so there are 3+1 digits in the one thousand we use as scale, and Log(5 700 000)=6.75 so there are 6+1 digits in the original number; since we scale by 1 000 in this simplified example, we want to find out to which number we have to divide the original value to get the scaled value, and that is Scale^Trunc(6.75/3), aka 1 000 ^ 2, aka 1 000 000. We therefore get 5 700 000 / 1 000 000 = 5.7 to which we append the appropriate "M" (i.e. mega-) suffix later on, using a similar approach.