text:
They call me Inspector Morse
A string is dot-heavy when its morse representation contains more dots than dashes. For example, the letter E is a single dot, which means it is Dot-heavy.
Input

The input string will only contain characters in the range of [a-z] or [A-Z]. You can decide if they should all be upper case, or all lower case. AAA is fine, aaa is fine, aAa is not.
The input string will always be at least 1 character in length.
You may assume that input strings will never have an equal amount of dots and dashes.

Output
You should return Truthy for inputs that contain more dot characters.
You should return Falsy for inputs that contain more dash characters.
Edit: I will allow a positive value for dot and a negative value for dash as well.
Test cases
| input | morse representation | result          |
|------------------------------------------------|
| S     | ...                  | Truthy          |
| k     | -.-                  | Falsy           |
| HELLO | .... . .-.. .-.. --- | Truthy          |
| code  | -.-. --- -.. .       | Falsy           |

Reference

This is code-golf. Shortest code in bytes wins.

Related

Can we return a value above 0 for dotheavy and a negative value for dash-heavy?

@EmbodimentofIgnorance That works for me, as long as you specify it in your post. I don't think it usually passes the truthy falsy test but it feels like a good solution in this case so I will allow it

APL (Dyalog Extended), 24 15  bytesSBCS
-9 thanks to Ven
Anonymous tacit prefix function taking uppercase as argument.
>/'.-'⍧∊∘⌂morse

Try it online!
⌂morse convert to list of Morse strings
∘ then
∊ ϵnlist (flatten)
'.-'⍧ count the number of dots and dashes in that
>/ more dots than dashes? (lit. greater-than reduction)

why not have Extended preload dfns by default?

@ngn It is now built-in

x86-16 machine code, 42 bytes
00000000: fc32 e4bb 1c01 ac24 1fd0 e8d7 7206 51b1  .2.....$....r.Q.
00000010: 04d2 e859 240f 2c03 02e0 e2ea c353 4452  ...Y$.,......SDR
00000020: 7412 5130 3146 2452 3100                 t.Q01F$R1.

Listing:
FC          CLD                 ; LODS string functions forward
32 E4       XOR  AH, AH         ; running sum = 0
BB 0119     MOV  BX, OFFSET TBL-1 ; offset data by -1 byte for one-based index
        LETTER_LOOP:
AC          LODSB               ; load next char from DS:SI into AL, advance SI
24 1F       AND  AL, 1FH        ; convert ASCII letter to one-based index
D0 E8       SHR  AL, 1          ; divide index by 2, CF=1 if odd index
D7          XLAT                ; look up letter in table
72 06       JC   ODD            ; odd index use low nibble; even use high
51          PUSH CX             ; save loop counter
B1 04       MOV  CL, 4          ; set up right shift for 4 bits
D2 E8       SHR  AL, CL         ; bit shift right
59          POP  CX             ; restore loop counter
        ODD:
24 0F       AND  AL, 0FH        ; isolate low nibble
2C 03       SUB  AL, 3          ; dash/dot difference remove +3 positive bias
02 E0       ADD  AH, AL         ; add difference to sum, set result flags
E2 EA       LOOP LETTER_LOOP    ; loop until end of input
C3          RET                 ; return to caller

        TBL:
            DB   53H, 44H, 52H, 74H, 12H, 51H
            DB   30H, 31H, 46H, 24H, 52H, 31H, 30H

Explanation
Input: string at DS:SI (upper or lower case OK).
Output: Truthy if SF == OF (use JG or JL to test).
The letter difference table values are stored as binary nibbles, so only takes 13 bytes in total. One byte can be saved by converting the input ASCII into a one-based index ('a' = 1, etc), however that would offset the table lookup index by one byte, not one nibble. Fortunately for us, the opcode of the previous RET instruction is C3, so that 3 is used for the 'a' = 1 nibble index.
Example Output:

I might be missing something, but does that macro not assume AH = 0 upon entry? Granted, that assumption is valid when using the test program.

Good eye!  The assumption is based on DOS execution initial startup register values, which for almost all versions of DOS is 0000h for AX source: http://www.fysnet.net/yourhelp.htm

From one assembly golfer to another: nice! Extra style points for using purely 8088-compatible instructions. That's a platform where code golfing is largely equivalent to optimization, and truly a lost art. Nice use of XLAT to do exactly what it is meant to do. If you were actually optimizing for speed over size, you'd want to do WORD-sized lookups. This is still a speed win even on the 8088 with its anemic 8-bit external bus, because you are doubling the throughput without increasing the code size, save for an XCHG instruction or two.

@CodyGray thanks!  It's always fun when a challenge lines up nicely with the platform and instruction set. Plus it is neat when you can accomplish something on the original PC's 8088 in 1 byte (such as XLAT), even though it takes 6 bytes to do a bitwise shift right 4 places (inside a LOOP).

Yup. For performance, you would definitely want to do 4 separate shifts by 1, eliminating the push and pop. It's not even that many more bytes (+2), so overall a net win, but not good for golfing. The real fun comes when the challenge doesn't line up with the ISA, and you have to stretch your mind to find new, innovative ways of applying the existing building blocks. The 1-byte string instructions are really nice on 8088 for performance and also golfing. I use them in real code. XLAT is one I don't often find much use for, I guess because modern architectures have biased me against LUTs.

Java (JDK), 131 124 110 84 64 bytes
Interestingly, "dot" is dash-heavy and "dash" is dot-heavy.
Takes input in all caps as an IntStream (scroll down for a version with an actual String for an extra 8 bytes). I've had quite a lot of help golfing this one: Thanks to Expired Data for golfing 20 bytes, to Neil for golfing 26 bytes, to Olivier Grégoire for golfing 18 bytes and to Kevin Cruijssen for golfing 2 bytes.
Contains 26 unprintable characters inside the double quotes.
c->c.map(a->"".charAt(a-65)-4).sum()>0

Try it online!
Ungolfed:
c -> // lambda taking input as an IntStream in upper case and returning a boolean
  c.map(a -> "" // map each character's ASCII value to its net dot impact (unprintable characters here)
    .charAt(a - 65) // translate the ASCII code into a zero-based index into the above string (65 is 'A')
    - 4) // unprintables are > 0, this restores the proper values
  .sum() > 0 // add up all the values, positive sum indicates a dot-heavy input string

Java (JDK), 131 124 110 84 72 bytes
For purists; takes input as a String. Thanks to Expired Data for golfing 20 bytes, to Neil for golfing 26 bytes and to Olivier Grégoire for golfing 10 bytes.
s->s.chars().map(a->"".charAt(a-65)-4).sum()>0

Try it online.
Ungolfed:
s -> // lambda taking input as a String in upper case and returning a boolean
  s.chars() // convert to a stream of characters
  .map(a -> "" // map each character's ASCII value to its net dot impact (unprintable characters here)
    .charAt(a - 65) // translate the ASCII code into a zero-based index into the above string (65 is 'A')
    - 4) // unprintables are > 0, this restores the proper values
  .sum() > 0 // add up all the values, positive sum indicates a dot-heavy input string

124 bytes

Make that 111 bytes

Nice! One more byte can be saved by using int a instead of char a.

Why not use "35344527512513031462452313".charAt(a-65)-51?

66 bytes

Or 74 bytes, if you're a purist

@OlivierGrégoire Your 66-byte is actually 65, since you forgot to remove the trailing semi-colon. 1 more byte can be saved by using unprintable characters however: 64 bytes

@KevinCruijssen Correct. Thank you for pointing that I (once more) forgot to remove that semicolon, and I thought about doing the unprintable characters, but a quick analysis made me think it wasn't possible. I need to get my actual unusable characters straight ;)

@OlivierGrégoire I think the -4 is the only possibility with the unprintables. Unprintable 0 isn't possible to use, so using the current digits as characters and then -3 isn't an option. And characters for 9 and 10 would become \t and \n, only increasing instead of decreasing the byte-count.

Jelly, 21 bytes
Oị“ÆġwıMƥ)ɠịṙ{’D¤Æm>4

Try it online!
How?
Oị“ÆġwıMƥ)ɠịṙ{’D¤Æm>4 - Link: list of characters ([A-Z]), S
                ¤     - nilad followed by link(s) as a nilad:
  “ÆġwıMƥ)ɠịṙ{’       -   base 250 integer = 14257356342446455638623624
               D      -   to decimal digits
                      -   -- that is the number of dots less the number of dashes plus 4
                      -      ... for each of OPQRSTUVWXYZABCDEFGHIJKLMN
O                     - ordinals of S   e.g. "ATHROUGHZ" -> [65,84,72,82,79,85,71,72,90]
 ị                    - index into (1-indexed & modular, so O gets the 79%26 = 1st item
                      -                                  or A gets the 65%26 = 13th item
                 Æm   - arithmetic mean
                   >4 - greater than 4?

C# (Visual C# Interactive Compiler), 47 bytes
n=>n.Sum(i=>("[E[LduRgmQSMK"[i%13]>>i%2*3)%8-3)

Uses Level River St's 'magic string'. Be sure to upvote their solution as well!
It's not everyday C# beats Ruby, Python, Javascript, C, Retina, and Perl!
Try it online!

05AB1E, 22 21 bytes
Saved a byte thanks to Kevin Cruijssen
SA•U(Õþć6Δ
»›I•‡3-O.±

Try it online!
Explanation
•U(Õþć6Δ
»›I•

is 35344527512513031462452313 compressed to base 255.
S              # split input into list of chars
       ‡       # transliterate
 A             # the lowercase alphabet
  •...•        # with the digits from the compressed string
        3-     # subtract 3 from each
          O    # then sum the result
           .±  # and take the sign

You can save a byte by replacing the map with S.

@KevinCruijssen: Thanks! I was sure I had tried that, but apparently not :)

Just for fun: the string usdgpsahsoaboutlopezgbidol (concatenation of 8 words from the dictionary) can be used to get the values: for each character $c$ in there: v = ord(c)*3%83%8.

@Arnauld: Interesting! How did you find that out? Not by hand I hope :P

I brute-forced all word pairs and the longest match was aboutlopez. I then looked for other matches with the same multiplier and modulo. (So it's absolutely not guaranteed to be optimal.)

JavaScript (Node.js),  69 68  65 bytes
-3 bytes thanks to @l4m2
Expects the input string in uppercase. Returns \$0\$ or \$1\$.
s=>!Buffer(s).map(n=>s='30314624523133534452741251'[n%26]-4-~s)<s

Try it online!

65

Jelly, 23 bytes
9“¡ȷṡẓh)ėḂYF@’ḃ_4ị@OS0<

Try it online!

C (gcc), 84 82 81 79 75 bytes
Assumes all caps.
r;f(char*s){for(r=0;*s;r+="+-+,,-*/--*-)+(+),.*,-*+)+"[*s++%65]-43);s=r>0;}

Try it online!

Python 2, 73 70 69 bytes
lambda s:sum(int(`0x21427b563e90d7783540f`[ord(c)%25])-3for c in s)>0

Try it online!
Uppercase only
-3 bytes, thanks to Erik the Outgolfer

Both upper- and lowercase version:
Python 2, 73 71 bytes
lambda s:sum(int(oct(0x1d7255e954b0ccca54cb)[ord(c)%32])-3for c in s)>0

Try it online!

Stax, 20 bytes
ÉBÜ◙ƒ╣<Hf6─òÉ¼säS╗◄↔

Run and debug it
Unpacked, ungolfed, and commented, it looks like this.
"45D.J57KBJa`"I"    string literal with code points [52 53 68 46 74 53 55 75 66 74 97 34 73]
$                   flatten to string "52536846745355756674973473"
;                   push input
@                   get string characters at indices
                    (using input codepoints as indices; lookups wrap around)
:V                  arithmetic mean
53>                 is greater than 53

Run this one

Ruby, 64 bytes
->s{n=0;s.bytes{|i|n+=("[E[LduRgmQSMK"[i%13].ord>>i%2*3)%8-3};n}

Try it online!
Uses a 13-byte magic string, 2 numbers 0..7 encoded in each byte. Subtract 3 for a range -3..4.
The ASCII code for A (and also N) taken modulo 13 is by coincidence, zero.

GNU sed, 113 bytes
Accepts lower-case inputs, one per line.  Output is 1 for dot-heavy or 0 for dash-heavy.  Since the question didn't specify the output for exactly balanced lines, I produce an empty line in that case.
s/[acnpxz]//g
y/bdfgjklqruvwy/ieitmtimeeitm/
:e
s/h/ii/
s/s/ie/
s/i/ee/
s/o/mt/
s/m/tt/
s/et\|te//
te
/e/c1
/t/c0

First, remove the letters which are themselves balanced.
Replace unbalanced letters with their equivalents (e.g. Q is --·-, so its net contribution is --, i.e. M).
Expand these equivalents into the corresponding count of E or T.
Remove paired E and T
If we have an E remaining, we're dot-heavy; if we have a T remaining, we're dash-heavy; otherwise (nothing remaining) evenly balanced.

Retina 0.8.2, 51 bytes
T`L`35344527412513031462452313
.
$*<>>>
+`<>|><

^<

Try it online! Link includes test cases. Only accepts upper case (+6 bytes for mixed case). Shamelessly stealing @Arnauld's string but I was going to use the same algorithm anyway. Explanation:
T`L`35344527412513031462452313
.

Change each letter into the difference in numbers of dots and dashes, plus three, so O=0 and H=7.
$*<>>>

Represent the difference as that number of <s and three >s. (Sadly I can't use dots because they're special in regex.)
+`<>|><

Remove matched pairs of <s and >s.
^<

Check whether there are still any dots left.

Bash+coreutils,  64 60 bytes
tr a-z 35344526512513031462452313|sed s/./\&z-+/g|dc -eIK?^p

Try it online!
Takes a string in lowercase, outputs zero for falsy, nonzero for truthy
Explanation
Uses tr and sed to create a dc program that looks like (for the example input 'hello'):
IK6z-+4z-+5z-+5z-+0z-+^p

IK     Push 10, then 0 to the stack
6z-+  Push 6 (three more than the dots minus dashes in 'h'), subtract 3, and accumulate
...    Do the same for all other letters, so the stack now has the total dots minus dashes
^      Raise 10 to this power - precision is zero so this turns negative/positive to falsy/truthy
p      Print result

Golfed two bytes by just putting the dc in the pipeline rather than use command substitution, then another byte by replacing <space>3 with z (conveniently, I have 3 items on the stack at that point!) and another byte by replacing the quotes around my sed program with a single backslash to escape the &

R,  74  70 bytes
f=utf8ToInt;sum(f("42433250265364746315325464")[f(scan(,''))-96]-52)<0

input should be lower case, returns TRUE or FALSE
Try it online

TI-BASIC (TI-84), 111 bytes
:Ans→Str1:"ABCDEFGHIJKLMNOPQRSTUVWXYZ→Str2:"35344527512513031462452312→Str3:0<sum(seq(expr(sub(Str3,inString(Str2,sub(Str1,X,1)),1)),X,1,length(Str1))-3

I used the same string for determining dot-heaviness as some of the other answers.
Program returns truthy (1) if the input string is dot-heavy, falsy (0) if not.
Input string must be in all-caps.
Input is stored in Ans.  Output is stored in Ans and is automatically printed out when the program completes.
Ungolfed:
:Ans→Str1
:"ABCDEFGHIJKLMNOPQRSTUVWXYZ→Str2
:"35344527512513031462452312→Str3
:0<sum(seq(expr(sub(Str3,inString(Str2,sub(Str1,X,1)),1)),X,1,length(Str1))-3

Example:
"HELLO
HELLO
prgmCDGF3
           1
"CODE
CODE
prgmCDGF3
           0

Explanation:
(TI-BASIC doesn't have comments, assume that ; indicates a commment)
:Ans→Str1                          ;store the input into Str1
:"ABCDEFGHIJKLMNOPQRSTUVWXYZ→Str2  ;store the uppercase alphabet into Str2
:"35344527512513031462452312→Str3  ;store dot-dash+3 for each letter into Str3

:0<sum(seq(expr(sub(Str3,inString(Str2,sub(Str1,X,1)),1)),X,1,length(Str1))-3 ;full logic

   sum(                                                                       ;sum the elements of
       seq(                                                               )    ;the list evaluated by
                sub(                                    )                       ;the substring of
                    Str3,                                                        ;Str3
                         inString(                  ),                           ;at the index of
                                       sub(        )                              ;the substring of
                                           Str1,                                   ;Str1
                                                X,                                 ;starting at X
                                                  1                                ;of length 1
                                  Str2,                                           ;in Str2
                                                      1                          ;of length 1
           expr(                                        ),                       ;converted to an integer
                                                          X,                    ;using X as the increment variable
                                                            1,                  ;starting at 1
                                                              length(Str1)      ;ending at the length of Str1
                                                                           -3   ;then subtract 3 from all elements in the list
  0<                                                                           ;then check if the sum is greater than 0
                                                                               ;implicitly output the result

Note:  The byte count of a program is evaluated using the value in [MEM]>[2]>[7] (124 bytes) then subtracting the length of the program's name, CDGF3, (5 bytes) and an extra 8 bytes used for storing the program:
124 - 5 - 8 = 111 bytes

Thunno 2, 21 bytes
Ạ»œfḅ“ƈ©OMṾȤØ»ṆdN3-Sʂ

Attempt This Online!
Port of Emigna's 05AB1E answer.
Explanation
Ạ»œfḅ“ƈ©OMṾȤØ»ṆdN3-Sʂ  # Implicit input
Ạ                      # Push the lowercase alphabet
 »œfḅ“ƈ©OMṾȤØ»         # Push compressed integer 35344527512513031462452313
              Ṇ        # Transliterate the input string (^^ --> ^)
               dN      # Convert to a list of digits
                 3-    # Subtract 3 from each
                   S   # Sum the list
                    ʂ  # Sign of the sum
                       # Implicit output

Morsecco 175 bytes
morsecco has a small advantage here: it knows morse code by nature. Do . - .-. to Read the stdin (special address -), -.- .- to Konvert from ·Text, -.- -- to Konvert to Morse and you already have the morse transcript. But then the work begins!
. - .-. -.- .- -.- -- . . - .
-- - -.-. - - - . . -... -.. --.. --.
- .- . . - . -.-. . - .. .- - . -- --.. --.
. - .- - .. .- - . - - .-.. --.. --.
- .- --.
- .- -.-. - ---

One funny golfing trick in the third line (handling of space or dash): Concatening a dot in front of the character turns the space into a null . , but the dash into a minus 1 (.-), so this value can simply be added to the total count.
The `Output is simply the first character of the count, which happens to be a dot for negative numbers and a dash for positive numbers, so the output is the character with less occurrences.

Perl 5 -pF, 53 bytes
$p+=y/a-z/35344526512513031462452313/r-3for@F;$_=$p>0

Try it online!

C++ (compiled with Visual Studio 2017) 171bytes
int f(string i){const char*c="1322131421130102123023121211210120032121323101112232";int j=0,h[2]={0};while(j<sizeof(i)/28)*h+=c[i[j]-97],h[1]+=c[i[j++]-71];return*h>h[1];}

if we take the main program that exists for test purposes into account as well its more.
this is the ungolfed "tidy" variant
#include "stdafx.h"
int main()
{
    const int dotCount[] = {1,3,2,2,1,3,1,4,2,1,1,3,0,1,0,2,1,2,3,0,2,3,1,2,1,2};
    const int dashCount[] = {1,1,2,1,0,1,2,0,0,3,2,1,2,1,3,2,3,1,0,1,1,1,2,2,3,2};
    std::cout << "Enter String:\n";
    std::string input;
    std::cin >> input;
    int inputsHeavyness[2] = { 0 };
    for(int i = 0;i < sizeof(input)/sizeof(std::string);i++)
    {
        inputsHeavyness[0] += dotCount[input[i] - 'a'];
        inputsHeavyness[1] += dashCount[input[i] - 'a'];
    }
    if (inputsHeavyness[0] > inputsHeavyness[1])
    {
        std::cout << "Dot Heavy\n";
    }
    else
    {
        std::cout << "Dash Heavy or Neutral\n";
    }
    return 0;
}

assumes all lowercase

You may want to add a TIO link. (Also, I think you have a typo in the ungolfed code: this 22 should be 2.)

yeah this may well be a typo. i guess i fixed that in the golfed version though. tio well i have no clue of that stuff (i think i looked at it once and it didnt feature the compiler i m using so results between vs2017 and tio would likely vary? no good at all)

You can just add #include <iostream> and using std::string; in the header section of TIO to get this working with gcc. (The header and footer sections are designed to make your code actually testable but do not count towards the total number of bytes.)

145 bytes. Results may indeed vary between VS and TIO. It sometimes varies for me too, and I am actually using GCC (albeit MinGW).

mmm automatic variables yeah one can use these if we are talking a recentish c++release (i actually havent used c++ in a long time and never knew all of it) so great you figured that out for me. the new syntax for the for loop is greek to me atm...

@ceilingcat you seemingly enjoy golfing my (?poor?) answers. and in all honesty i have a hard tine graspibg what you do. feel free to fork the answer and put an explainatory non-golf version along side

Tweak of @ceilingcat for 131 bytes

Building on @gastropner 111 bytes Combined both arrays into one; "132... and "112... become "353... and 51 is the ASCII value of 3

89 bytes https://tio.run/##NY1NDoMgFAb3noLQDaQxEZGainqRpgsCaEkVjMLKeHbrX1fzzSzek8MQt1KuN2NlF5QGpXGTH7Xo6@jfyi0Y29ZRmDaAyauiOBNfjfWgQacBg@fdRZXwxo1o36owWNwrSBnNMpbmjKSM0ISS7JFuTgmFLxU/83fMCB@1D6MFok74clzuhbEIzxE4n0oXfFk2CE4QH/xe/Oiuc9eWTmmIebSsPw by moving into the header section what is intended to be there

Factor, 66 bytes
: f ( s -- ? ) >morse [ [ 45 = ] count ] [ [ 46 = ] count ] bi < ;

Try it online!

c (118 characters)
returns a positive value for over-dot-ness and negative value for over-dash-ness
int n(char* c){int v=25124858,d=3541434,i=0,o=0;for(;c[i]!=0;i++)o=(1&(v>(c[i]-65)))>0?(1&(d>>(c[i]-65)))>0?o+1:o-1:o;return o;}

un-golfed
int n(char* c)
{
  // Bitwise alpha map:
  // more dots = 1
  // more dashes or equal = 0
  int d=3541434;
  // validation bit map.
  // dot/dash heavy = 1
  // even = 0
  int v=25124858;
  int i=0,o=0;
  for(;c[i]!=0;i++)   // iterate through all values
  {
    // There is no way to make this pretty
    // I did my best.
    // If the little endian validation bit corresponding
    // to the capitol letter ascii value - 65 = 0,
    // the output does not increment or decrement.
    // If the value is one it increases or decreases based
    // on the value of the d bitmap.
    o=(1& ( v > (c[I] - 65))) > 0 ?
      (1 & (d >> (c[I] - 65))) > 0 ?
        o + 1 :
        o - 1 :
      o;
  }
  return o;
}

I must confess I do not fully understand the comparison 1& ( v > (c[I] - 65)), which is the same as v > c[I] - 65, which I cannot imagine is ever false, so we could remove that whole thing while riffing on @ceilingcat for 56 bytes

Python 2, 90 86 bytes
import morse
s=''.join(morse.string_to_morse(input()))
print s.count('.')>s.count('-')

worked on my local with the morse library.
-4 bytes. Thanks for the tip @JoKing!
Also, it's 1 byte more if it's in Python 3.
Python 3, 87 bytes
import morse
s=''.join(morse.string_to_morse(input()))
print(s.count('.')>s.count('-'))

Though the question assumes the number of '.'s and '-'s will not be equal; in case they are equal, this code will return True.

I mean, you can use input instead of raw_input if you want...

@JoKing i tried. It was throwing an error and hence had to resort to raw_input

you just have to put quotes around the string, since input evals STDIN before passing it to the program

That is a very fair point. I feel stupid for missing that out! :3

MathGolf, 22 bytes
{▄="Yⁿ∩┐↑rⁿ¼~<↔"$▒3-§+

Try it online!
Uses the same method as many other answers, where ⁿ∩┐↑rⁿ¼~<↔" represents the magic number 35344527512513031462452313.

Vyxal, 19 bytes
n»¨g±„ẇ⟇PNĖṡø»Ŀ3-∑±

Try it Online!
Saying the same joke but louder shorter is fun :p
Ports 05AB1E
Explained
n»¨g±„ẇ⟇PNĖṡø»Ŀ3-∑±
n                     # Lowercase alphabet
 »¨g±„ẇ⟇PNĖṡø»        # 35344527512513031462452313
              Ŀ       # transliterate
               3-     # - 3
                 ∑±   # sign of sum

C++, 76 bytes
This assumes the input is in ASCII (or any encoding that's compatible for letters, e.g. UTF-8 or ISO-8859.1).  Input may be in either case.
[](auto s,int&i){i=0;for(int c:s)i+="DDFDEEECHFBCFBDADBEGCEFCDBD"[c&31]-68;}

It's a function that accepts a string and and integer reference.  The integer gets assigned a positive number (truthy) for dot-heavy input or a negative number (falsey) for dot-light input.  Balanced input returns zero.
The magic string encodes the weight of each of A..Z, offset by D (i.e. D represents a Morse-balanced letter).  That's the 68 we subtract from each value.
I did attempt encoding two Morse symbols per char in the magic string, but the cost of unpacking was too great.
Test program:
auto f = [](auto s,int&i){i=0;for(int c:s)i+="DDFDEEECHFBCFBDADBEGCEFCDBD"[c&31]-68;};

#include <iostream>
#include <string>

int main(int argc, char **argv)
{
    int i;
    while (*++argv) {
        std::string s{*argv};
        f(s, i);
        std::cout << s << ": " << i << '\n';
    }
}

Output:
./181318 S k HELLO code
S: 3
k: -1
HELLO: 6
code: -1

Nibbles, 16.5 bytes (33 nibbles)
+.$-=$`D8 3 2954b0ccca54cb75c957

Attempt This Online!
Approach based on Emigna's O5AB1E answer.
Input is lowercase.  Outputs the dot-dash sum: positive values are truthy in Nibbles, negative values (as well as zero) are falsy.
To directly compare to the Vyxal, Thunno, O5AB1E etc. answers, add 2 nibbles (+1 byte; prefix  `$) to output the sign of the sum, although this isn't necessary here due to Nibbles' truthy/falsy convention.
      `D    2954b0ccca54cb75c957    # interpret hex 2954b0ccca54cb75c957
        8                           # in base 8
                                    # (digits: 51251303146245231335344527)
 .$                                 # now map over the input
    =$                              # getting the digit at the modular index
                                    # of each input codepoint
   -      3                         # subtract 3 from each
+                                   # and sum the result

Scala 3, 91 bytes
A port of @Level River St's Ruby answer in Scala.

Golfed version. Attempt This Online!
s=>{var n=0;s.getBytes.foreach{i=>n+=(("[E[LduRgmQSMK".apply(i%13).toInt>>(i%2*3))%8-3)};n}

Ungolfed version. Attempt This Online!
object Main {
  def main(args: Array[String]): Unit = {
    val f = (s: String) => {
      var n = 0
      s.getBytes.foreach { i =>
        val index = i % 13
        val shift = i % 2 * 3
        val char = "[E[LduRgmQSMK"(index)
        n += ((char.toInt >> shift) % 8 - 3)
      }
      n
    }

    List("S", "K", "HELLO", "CODE").foreach { j =>
      println(s"$j -> ${f(j)}")
    }

    ('A' to 'Z').foreach { j =>
      println(s"$j -> ${f(j.toString)}")
    }
  }
}

GNU sed -E, 112 bytes
An approach could not differ more from the existing sed solution, but beating it by only one byte! This one builds a map from +4 dots to +3 dashes then two s commands taking turns in moving one of their side down until one of them has no moves left:
s/^/H S IFLV EDRU :_GKWT_JQYM_O!/
:1
s/((\S)\S* (.).*)\2/\1\3/
T-
s/((.)_[^_]*([^_]).*!.*)\3/\1\2/
t1
c.
q
:-
c-

Try it online!


text:
Am I not good enough for you?
Background:
The current Perfect Numbers challenge is rather flawed and complicated, since it asks you to output in a complex format involving the factors of the number. This is a purely decision-problem repost of the challenge.
Challenge
Given a positive integer through any standard input format, distinguish between whether it is perfect or not.
A perfect number is a number that is equal to the sum of all its proper divisors (its positive divisors less than itself). For example, \$6\$ is a perfect number, since its divisors are \$1,2,3\$, which sum up to \$6\$, while \$12\$ is not a perfect number since its divisors ( \$1,2,3,4,6\$ ) sum up to \$16\$, not \$12\$.
Test Cases:
Imperfect:
1,12,13,18,20,1000,33550335

Perfect:
6,28,496,8128,33550336,8589869056

Rules

Your program doesn't have to complete the larger test cases, if there's memory or time constraints, but it should be theoretically able to if it were given more memory/time.
Output can be two distinct and consistent values through any allowed output format. If it isn't immediately obvious what represents Perfect/Imperfect, please make sure to specify in your answer.

Why is 1 not a perfect number? 1 == 1.

@Tvde1 Proper divisors have to less than the number, otherwise no number other than 1 would be perfect, since every number is divisible by 1 and itself. The sum of proper divisors of 1 is 0

Are we allowed to assume there are no odd perfect numbers?

@Grimy Only if you can prove so. Good luck! (though I'm wondering how that would save bytes)

So no, too bad. It would cut the size of an ECMA regex answer by a factor of about 3.

"Output can be two distinct and consistent values" - may we not use "truthy vs falsey" here (e.g. for Python using zero vs non zero; a list with content vs an empty list; and combinations thereof)?

Brachylog, 4 bytes
fk+?

Try it online!
The predicate succeeds for perfect inputs and fails for imperfect inputs, printing true. or false. if run as a complete program (except on the last test case which takes more than a minute on TIO).
        The input's
f       factors
 k      without the last element
  +     sum to
   ?    the input.

I like how the code says fk :x

Neim, 3 bytes

Try it online!
(I don't actually know how to run all of the test cases at once, since I started learning Neim about fifteen minutes ago, but I did check them individually.)
Prints 0 for imperfect, 1 for perfect.
      Pop an int from the stack and push its proper divisors,
       implicitly reading the int from a line of input as the otherwise absent top of the stack.
      Pop a list from the stack and push the sum of the values it contains.
      Pop two ints from the stack and push 1 if they are equal, 0 if they are not;
       implicitly reading the same line of input that was already read as the second int, I guess?
       Implicitly print the contents of the stack, or something like that.

"I guess?"; "or something like that.". When you're not even sure what you've written yourself, haha. ;) But yes, that's indeed how it works. I don't know Neim, but using the input implicitly like that and outputting implicitly at the end implicitly, is similar in 05AB1E.

How is `` 1 byte? Does Neim use only 128 such non-standart characters?

@kajacx Neim has its own code page. Therefore, each of the 256 characters present in the codepage can be encoded using 1 byte.

R, 33 29 bytes
!2*(n=scan())-(x=1:n)%*%!n%%x

Try it online!
Returns TRUE for perfect numbers and FALSE for imperfect ones.

What do the 2 !s in a row get you?

@CTHall I misread the spec; they originally mapped 0 (perfect) to FALSE and nonzero to TRUE but I removed one of them to reverse the mapping. It's a useful golfing trick to cast from numeric to logical, often in conjunction with which or [.

Japt -!, 4 bytes
¥â¬x
-----------------
            Implicit Input U
¥           Equal to
   x        Sum of
 â          Factors of U
  ¬         Without itself

For some reason ¦ doesnt work on tio so I need to use the -! flag and ¥ instead
Try it online!

That's not a TIO issue; U doesn't get auto-inserted before !.

Jelly, 3 bytes
Æṣ=

Try it online!

Python 3, 46 bytes
lambda x:sum(i for i in range(1,x)if x%i<1)==x

Try it online!
Brute force, sums the factors and checks for equality.

Using the comprehension condition as a mask for your iteration variable would save a byte.

Since you can return truthy for an imperfect number, lambda x:sum(i for i in range(1,x)if x%i<1)^x should work as well.

C# (Visual C# Interactive Compiler), 46 bytes
n=>Enumerable.Range(1,n).Sum(x=>n%x<1?x:0)^n*2

Returns 0 if perfect, otherwise returns a positive number. I don't know if outputting different types of integers are allowed in place of two distinct truthy and falsy values, and couldn't find any discussion on meta about it. If this is invalid, I will remove it.
Try it online!
C# (Visual C# Interactive Compiler), 49 47 bytes
n=>Enumerable.Range(1,n).Sum(x=>n%x<1?x:0)==n*2

Try it online!

Octave, 25 bytes
@(n)~mod(n,t=1:n)*t'==2*n

Try it online!
Explanation
@(n)~mod(n,t=1:n)*t'==2*n

@(n)                        % Define anonymous function with input n
             1:n            % Row vector [1,2,...,n]
           t=               % Store in variable t
     mod(n,     )           % n modulo [1,2,...,n], element-wise. Gives 0 for divisors
    ~                       % Logical negate. Gives 1 for divisors
                  t'        % t transposed. Gives column vector [1;2;...;n]
                 *          % Matrix multiply
                      2*n   % Input times 2
                    ==      % Equal? This is the output value

Python, 45 bytes
lambda n:sum(d*(n%d<1)for d in range(1,n))==n

True for perfect; False for others (switch this with == -> !=)
Try it online!
 44 42  41 bytes (-2 thanks to ovs) if we may output using "truthy vs falsey":
f=lambda n,i=1:i/n or-~f(n,i+1)-(n%i<1)*i

(falsey (0)) for perfect; truthy (a non-zero integer) otherwise

If the second output format is valid, this can be done in 42 bytes.

@ovs ah, nicely done.

@ovs ..and another saved from that - thanks!

Labyrinth, 80 bytes
?::`}:("(!@
perfect:
{:{:;%"}
+puts; "
}zero: "
}else{(:
"negI"  _~
""""""{{{"!@

The Latin characters perfect puts zero else neg I are actually just comments*.
i.e. if the input is perfect a 0 is printed, otherwise -1 is.
Try it online!

* so this or this work too...
?::`}:("(!@               ?::`}:("(!@
       :                  BEWARE :
{:{:;%"}                  {:{:;%"}
+    ; "                  +LAIR; "
}    : "                  } OF : "
}    {(:                  }MINO{(:
"    "  _~                "TAUR"  _~
""""""{{{"!@              """"""{{{"!@

How?
Takes as an input a positive integer n and places an accumulator variable of -n onto the auxiliary stack, then performs a divisibility test for each integer from n-1 down to, and including, 1, adding any which do divide n to the accumulator. Once this is complete if the accumulator variable is non-zero a -1 is output, otherwise a 0 is.
The ?::`}:( is only executed once, at the beginning of execution:
?::`}:(                                                      Main,Aux
?       - take an integer from STDIN and place it onto Main  [[n],[]]
 :      - duplicate top of Main                            [[n,n],[]]
  :     - duplicate top of Main                          [[n,n,n],[]]
   `    - negate top of Main                            [[n,n,-n],[]]
    }   - place top of Main onto Aux                       [[n,n],[-n]]
     :  - duplicate top of Main                          [[n,n,n],[-n]]
      ( - decrement top of Main                        [[n,n,n-1],[-n]]

The next instruction, ", is a no-op, but we have three neighbouring instructions so we branch according to the value at the top of Main, zero takes us forward, while non-zero takes us right.
If the input was 1 we go forward because the top of Main is zero:
(!@                                                          Main,Aux
(   - decrement top of Main                             [[1,1,-1],[-1]]
 !  - print top of Main, a -1
  @ - exit the labyrinth

But if the input was greater than 1 we turn right because the top of Main is non-zero:
:}                                                           Main,Aux
:  - duplicate top of Main                         [[n,n,n-1,n-1],[-n]]
 } - place top of Main onto Aux                        [[n,n,n-1],[-n,n-1]]

At this point we have a three-neighbour branch, but we know n-1 is non-zero, so we turn right...
"%                                                           Main,Aux
"  - no-op                                             [[n,n,n-1],[-n,n-1]]
 % - place modulo result onto Main                   [[n,n%(n-1)],[-n,n-1]]
   - ...i.e we've got our first divisibility indicator n%(n-1), an
   -    accumulator, a=-n, and our potential divisor p=n-1:
   -                                                 [[n,n%(n-1)],[a,p]]

We are now at another three-neighbour branch at %.
If the result of % was non-zero we go left to decrement our potential divisor, p=p-1, and leave the accumulator, a, as it is:
;:{(:""}"                                                    Main,Aux
;          - drop top of Main                                [[n],[a,p]]
 :         - duplicate top of Main                         [[n,n],[a,p]]
  {        - place top of Aux onto Main                  [[n,n,p],[a]]
           - three-neighbour branch but n-1 is non-zero so we turn left
   (       - decrement top of Main                     [[n,n,p-1],[a]]
    :      - duplicate top of Main                 [[n,n,p-1,p-1],[a]]
     ""    - no-ops                                [[n,n,p-1,p-1],[a]]
       }   - place top of Main onto Aux                [[n,n,p-1],[a,p-1]]
        "  - no-op                                     [[n,n,p-1],[a,p-1]]
         % - place modulo result onto Main           [[n,n%(p-1)],[a,p-1]]
           - ...and we branch again according to the divisibility
           -    of n by our new potential divisor, p-1

...but if the result of % was zero (for the first pass only when n=2) we go straight on to BOTH add the divisor to our accumulator, a=a+p, AND decrement our potential divisor, p=p-1:
;:{:{+}}""""""""{(:""}                                       Main,Aux
;                      - drop top of Main                    [[n],[a,p]]
 :                     - duplicate top of Main             [[n,n],[a,p]]
  {                    - place top of Aux onto Main      [[n,n,p],[a]]
   :                   - duplicate top of Main         [[n,n,p,p],[a]]
    {                  - place top of Aux onto Main  [[n,n,p,p,a],[]]
     +                 - perform addition            [[n,n,p,a+p],[]]
      }                - place top of Main onto Aux      [[n,n,p],[a+p]]
       }               - place top of Main onto Aux        [[n,n],[a+p,p]]
        """""""        - no-ops                            [[n,n],[a+p,p]]
                       - a branch, but n is non-zero so we turn left
               "       - no-op                             [[n,n],[a+p,p]]
                {      - place top of Aux onto Main      [[n,n,p],[a+p]]
                       - we branch, but p is non-zero so we turn right
                 (     - decrement top of Main         [[n,n,p-1],[a+p]]
                  :    - duplicate top of Main     [[n,n,p-1,p-1],[a+p]]
                   ""  - no-ops                    [[n,n,p-1,p-1],[a+p]]
                     } - place top of Main onto Aux    [[n,n,p-1],[a+p,p-1]]

At this point if p-1 is still non-zero we turn left:
"%                                                           Main,Aux
"  - no-op                                             [[n,n,p-1],[a+p,p-1]]
 % - modulo                                          [[n,n%(p-1)],[a+p,p-1]]
   - ...and we branch again according to the divisibility
   -    of n by our new potential divisor, p-1

...but if p-1 hit zero we go straight up to the : on the second line of the labyrinth (you've seen all the instructions before, so I'm leaving their descriptions out and just giving their effect):
:":}"":({):""}"%;:{:{+}}"""""""{{{                           Main,Aux
:                                  -                   [[n,n,0,0],[a,0]]
 "                                 -                   [[n,n,0,0],[a,0]]
                                   - top of Main is zero so we go straight
                                   -  ...but we hit the wall and so turn around
  :                                -                 [[n,n,0,0,0],[a,0]]
   }                               -                   [[n,n,0,0],[a,0,0]]
                                   - top of Main is zero so we go straight
    ""                             -                   [[n,n,0,0],[a,0,0]]
      :                            -                 [[n,n,0,0,0],[a,0,0]]
       (                           -                [[n,n,0,0,-1],[a,0,0]]
        {                          -              [[n,n,0,0,-1,0],[a,0]]
                                   - top of Main is zero so we go straight
                                   -  ...but we hit the wall and so turn around
         (                         -             [[n,n,0,0,-1,-1],[a,0]]
          :                        -          [[n,n,0,0,-1,-1,-1],[a,0]]
           ""                      -          [[n,n,0,0,-1,-1,-1],[a,0]]
             }                     -             [[n,n,0,0,-1,-1],[a,0,-1]]
                                   - top of Main is non-zero so we turn left
              "                    -             [[n,n,0,0,-1,-1],[a,0,-1]]
               %                   - (-1)%(-1)=0     [[n,n,0,0,0],[a,0,-1]]
                ;                  -                   [[n,n,0,0],[a,0,-1]]
                 :                 -                 [[n,n,0,0,0],[a,0,-1]]
                  {                -              [[n,n,0,0,0,-1],[a,0]]
                   :               -           [[n,n,0,0,0,-1,-1],[a,0]]
                    {              -         [[n,n,0,0,0,-1,-1,0],[a]]
                     +             -           [[n,n,0,0,0,-1,-1],[a]]
                      }            -              [[n,n,0,0,0,-1],[a,-1]]
                       }           -                 [[n,n,0,0,0],[a,-1,-1]]
                        """""""    -                 [[n,n,0,0,0],[a,-1,-1]]
                                   - top of Main is zero so we go straight
                               {   -              [[n,n,0,0,0,-1],[a,-1]]
                                {  -           [[n,n,0,0,0,-1,-1],[a]]
                                 { -         [[n,n,0,0,0,-1,-1,a],[]]

Now this { has three neighbouring instructions, so...
...if a is zero, which it will be for perfect n, then we go straight:
"!@                                                          Main,Aux
"   -                                        [[n,n,0,0,0,-1,-1,a],[]]
    - top of Main is a, which is zero, so we go straight
 !  - print top of Main, which is a, which is a 0
  @ - exit the labyrinth

...if a is non-zero, which it will be for non-perfect n, then we turn left:
_~"!@                                                        Main,Aux
_     - place a zero onto Main             [[n,n,0,0,0,-1,-1,a,0],[]]
 ~    - bitwise NOT top of Main (=-1-x)   [[n,n,0,0,0,-1,-1,a,-1],[]]
  "   -                                   [[n,n,0,0,0,-1,-1,a,-1],[]]
      - top of Main is NEGATIVE so we turn left
   !  - print top of Main, which is -1
    @ - exit the labyrinth

JavaScript, 38 bytes
n=>eval("for(i=s=n;i--;)n%i||!(s-=i)")

Try it online!
(Last testcase timeout on TIO.)

@Arnauld Just forgot to remove the f= after converting from a recursive function.

Just out of curiosity, why not going with a recursive version? (It would be 34 bytes.)

@Arnauld because recursive version would simply failed for larger testcase due to stack overflow. Maybe I need some environments default to strict mode to make it work.

Fair enough, but your program doesn't have to complete the larger test cases (which I think is the default rule, anyway).

37

Ruby, 33 bytes
->n{(1...n).sum{|i|n%i<1?i:0}==n}

Try it online!

Java (JDK), 54 bytes
n->{int s=0,d=0;for(;++d<n;)s+=n%d<1?d:0;return s==n;}

Try it online!
Though for a strict number by number matching, the following will return the same values, but is only 40 bytes.
n->n==6|n==28|n==496|n==8128|n==33550336

Try it online!

The rules say Your program doesn't have to complete the larger test cases, if there's memory or time constraints, but it should be theoretically able to if it were given more memory/time.

@JoKing Does that mean that I can't use a Java int at all, but rather a BigInteger? Because Java has BigIntegers, but it won't ever have an int that's more than 31 bits as signed, which can't hold any other value than those represented here...

no, but if the program should still work if the int type was unbounded

@JoKing Ok, I switched the two solutions again to have the computation first.

TI-BASIC (TI-84), 30 23 bytes
:2Ans=sum(seq(Ans/Xnot(remainder(Ans,X)),X,1,Ans,1

Horribly inefficient, but it works.
Reducing the bytecount sped up the program by a lot.
Input is in Ans.
Output is in Ans and is automatically printed out when the program completes.
Explanation:
(TI-BASIC doesn't have comments, so just assume that ; makes a comment)
:2Ans=sum(seq(Ans/Xnot(remainder(Ans,X)),X,1,Ans    ;Full program

 2Ans                                               ;double the input
          seq(                                      ;generate a list
                                         X,          ;using the variable X,
                                           1,        ;starting at 1,
                                             Ans     ;and ending at the input
                                                     ;with an implied increment of 1
              Ans/X                                 ;from the input divided by X
                   not(                ),           ;multiplied by the negated result of
                       remainder(Ans,X)              ;the input modulo X
                                                     ;(result: 0 or 1)
      sum(                                          ;sum up the elements in the list
     =                                              ;equal?

Example:
6
            6
prgmCDGF2
            1
7
            7
prgmCDGF2
            0

Note:  The byte count of a program is evaluated using the value in [MEM]>[2]>[7] (36 bytes) then subtracting the length of the program's name, CDGF2, (5 bytes) and an extra 8 bytes used for storing the program:
36 - 5 - 8 = 23 bytes

x86 Assembly, 45 43 Bytes.
6A 00 31 C9 31 D2 41 39  C1 7D 0B 50 F7 F9 58 85
D2 75 F1 51 EB EE 31 D2  59 01 CA 85 C9 75 F9 39
D0 75 05 31 C0 40 EB 02  31 C0 C3

Explaination (Intel Syntax):
PUSH $0          ; Terminator for later
XOR ECX, ECX        ; Clear ECX
.factor:
    XOR EDX, EDX    ; Clear EDX
    INC ECX
    CMP ECX, EAX    ; divisor >= input number?
    JGE .factordone ; if so, exit loop.
    PUSH EAX        ; backup EAX
    IDIV ECX        ; divide EDX:EAX by ECX, store result in EAX and remainder in EDX
    POP EAX         ; restore EAX
    TEST EDX, EDX   ; remainder == 0?
    JNZ .factor     ; if not, jump back to loop start
    PUSH ECX        ; push factor
    JMP .factor     ; jump back to loop start
.factordone:
XOR EDX, EDX        ; clear EDX
.sum:
    POP ECX         ; pop divisor
    ADD EDX, ECX    ; sum into EDX
    TEST ECX, ECX   ; divisor == 0?
    JNZ .sum        ; if not, loop.
CMP EAX, EDX        ; input number == sum?
JNE .noteq          ; if not, skip to .noteq
    XOR EAX, EAX    ; clear EAX
    INC EAX         ; increment EAX (sets to 1)
JMP .return         ; skip to .return
.noteq:
    XOR EAX, EAX    ; clear EAX
.return:
RETN

Input should be provided in EAX.
Function sets EAX to 1 for perfect and to 0 for imperfect.
EDIT: Reduced Byte-Count by two by replacing MOV EAX, $1 with XOR EAX, EAX and INC EAX

I use a macro assembly so I don't know for sure but the comment"; divisor > input number" for me would be "; divisor >= input number"

Assembly has easy operations one could reduce instructions length puts all in a line, use indentation and comment every 10 20 asm instructions....

@RosLuP I've fixed the comment in the code (thanks), but I don't know what you mean with your second comment.

Regex (Perl/PCRE+(?^=)RME), 44 41 bytes
((\2x+?|^x\B)(?^=\2+$))++$(?^!(\2x+)\3+$)

Try it on replit.com
This was a port of my 48 byte .NET regex below, using lookinto instead of variable-length lookbehind. This makes it far faster, and able to scan all numbers from 0 to 9000 in about 7 seconds on my PC. It also enables somes golfs that aren't possible in the .NET version.
                   # No anchor needed, because \2 cannot be initially captured if
                   # matching is started anywhere besides the beginning.
# Sum as many divisors of N as we can (using the current position as the sum),
# without skipping any of them:
(
    (
        \2x+?      # \2 = the smallest number larger than the previous value of \2
                   # for which the following is true; add \2 to the running total
                   # sum of divisors.
    |
        ^x         # This must always be the first step. Add 1 to the sum of
                   # divisors, thus setting \2 = 1 so the next iteration can keep
                   # going.
        \B         # Exclude 1 as a divisor if N == 1. We don't have to do this for
                   # N > 1, because this loop will never be able to add N to the
                   # already summed divisors (as 1 will always be one of them).
    )
    (?^=\2+$)      # Assert that \2 is a divisor of N
)++                # Iterate the above at least once, with a possessive quantifier
                   # to lock in the match (i.e. prevent any iteration from being
                   # backtracked into).
$                  # Require that the sum equal N exactly. If this fails, the loop
                   # won't be able to find a match by backtracking, because
                   # everything inside the loop is done atomically.
# Assert that there are no more proper divisors of N that haven't already been summed:
(?^!               # Lookinto – Assert that the following cannot match:
    (\2x+)         # \3 = any number greater than \2
    \3+$           # Assert \3 is a proper divisor of N
)

Regex (Perl/PCRE+(?^=)RME), 45 bytes
^(?=(x(?=(x*))(?^=(?(?=\2+$)(\3?+\2))))+$)\3$

Try it on replit.com
This is a port of the largest-to-smallest 47 byte .NET regex below, using lookinto instead of variable-length lookbehind.
Regex (Perl/PCRE+(?^=)RME), 47 bytes
^(?=((?^0=(x*))(?^=(?(?=\2+$)(\3?+\2)))x)+$)\3$

Try it on replit.com
This is a port of the smallest-to-largest 47 byte .NET regex below, using lookinto instead of variable-length lookbehind. This one is less conducive to being ported, and I had to use (?^0=), lookinto accessing the in-progress current match. I've not decided how to finalize the current behavior of this, so it's likely this version will not work with a future version of lookinto.
Regex (.NET), 47 bytes
^(?=((?<=(?=(?(\3+$)((?>\2?)\3)))(^x*))x)+$)\2$

Try it online!
^(?=(x(?=(x*$)(?<=(?(^\2+)(\2(?>\3?))))))+$)\3$

Try it online!
This is based on my 44 byte abundant numbers regex, with is in turn based on Martin Ender's 45 byte abundant numbers regex. It was trivial to adapt to matching perfect numbers; just two small changes were needed, with a third change made for aesthetic purposes.
# For the purpose of these comments, the input number will be referred to as N.

^(?=                  # Attempt to add up all the divisors of N.
  (x                  # Cycle through all positive values of tail that are less than N,
                      # testing each one to see if it is a divisor of N. Start at N-1.
    (?=               # Do the below operations in a lookahead, so that upon popping back
                      # out of it our position will remaing the same as it is here.
      (x*$)           # \2 = tail, a potential divisor; go to end to that the following
                      # lookbehind can operate on N as a whole.
      (?<=            # Switch to right-to-left evaluation so that we can operate both
                      # on N and the potential divisor \2. This requires variable-length
                      # lookbehind, a .NET feature. Please read these comments in the
                      # order indicated, from [Step 1] to [Step 4].
        (?(^\2+)      # [Step 1] If \2 is a divisor of N, then...
          (           # [Step 2] Add it to \3, the running total sum of divisors:
                      #          \3 = \3 + \2
            \2        # [Step 4] Iff we run out of space here, i.e. iff the sum would
                      #          exceed N at this point, the match will fail, and the
                      #          summing of divisors will be halted. It can't backtrack,
                      #          because everything in the loop is done atomically.
            (?>\3?)   # [Step 3] Since \3 is a nested backref, it will fail to match on
                      #          the first iteration. The "?" accounts for this, making
                      #          it add zero to itself on the first iteration. This must
                      #          be done before adding \2, to ensure there is enough room
                      #          for the "?" not to cause the match to become zero-length
                      #          even if \3 has a value.
          )
        )
      )
    )
  )+                  # Using "+" instead of "*" here is an aesthetic choice, to make it
                      # clear we don't want to match zero. It doesn't actually make a
                      # difference (besides making the regex ever so slightly more
                      # efficient) because \3 would be unset anyway for N=0.
  $                   # We can only reach this point if all proper divisors of N, all the
                      # way down to \2 = 1, been successfully summed into \3.
)
\3$                   # Require the sum of divisors to be exactly equal to N.

Regex (.NET), 48 bytes
((?=.*(\1x+$)(?<=^\2+))\2|^x\B)+$(?<!^\3+(x+\2))

Try it online!
This is based on my 43 byte abundant numbers regex. It was interesting and somewhat tricky to adapt into a fully golfed perfect numbers regex.
                   # No anchor needed, because \1 cannot be initially captured if
                   # matching is started anywhere besides the beginning.
# Sum as many divisors of N as we can (using the current position as the sum),
# without skipping any of them:
(
    (?=            # Atomic lookahead:
        .*(\1x+$)  # \2 = the smallest number larger than the previous value of \1
                   # (which is identical to the previous value of \2) for which the
                   # following is true. We can avoid using "x+?" because the ".*"
                   # before this implicitly forces values to be tested in increasing
                   # order from the smallest.
        (?<=^\2+)  # Assert that \2 is a divisor of N
    )
    \2             # Add \2 to the running total sum of divisors
|
    ^x             # This must always be the first step. Add 1 to the sum of divisors,
                   # thus setting \1 = 1 so the next iteration can keep going
    \B             # Exclude 1 as a divisor if N == 1. We don't have to do this for
                   # N > 1, because this loop will never be able to add N to the
                   # already summed divisors (as 1 will always be one of them).
)+$                # Require that the sum equal N exactly. If this fails, the loop
                   # won't be able to find a match by backtracking, because everything
                   # inside the loop is done atomically.
# Assert that there are no more proper divisors of N that haven't already been summed:
(?<!               # Assert that the following, evaluated right-to-left, cannot match:
    ^\3+           # [Step 2] is a proper divisor of N
    (x+\2)         # [Step 1] Any value of \3 > \2
)

Regex (Perl / PCRE2 v10.35+), 64 60 57 bytes
This is a port of the 41 byte regex, emulating variable-length lookbehind using a recursive subroutine call.
(?=((\2x+?|^x\B)((?<=(?=^\2+$|(?3)).)))++$)(?!(\2x+)\4+$)

Try it online! - Perl
Attempt This Online! - PCRE2 v10.40+
                   # No anchor needed, because \2 cannot be initially captured if
                   # matching is started anywhere besides the beginning.
(?=
    # Sum as many divisors of N as we can (using the current position as the sum),
    # without skipping any of them:
    (
        (
            \2x+?      # \2 = the smallest number larger than the previous value of \2
                       # for which the following is true; add \2 to the running total
                       # sum of divisors.
        |
            ^x         # This must always be the first step. Add 1 to the sum of
                       # divisors, thus setting \2 = 1 so the next iteration can keep
                       # going.
            \B         # Exclude 1 as a divisor if N == 1. We don't have to do this for
                       # N > 1, because this loop will never be able to add N to the
                       # already summed divisors (as 1 will always be one of them).
        )
        ((?<=          # Define subroutine (?3); lookbehind
            (?=        # Circumvent the constant-width limitation of lookbehinds
                       # in PCRE by using a lookahead inside the lookbehind
                ^\2+$  # This is the payload of the emulated variable-length
                       # lookbehind: Assert that \2 is a divisor of N
            |
                (?3)   # Recursive call - this is the only alternative that can
                       # match until we reach the beginning of the string
            )
            .          # Go back one character at a time, trying the above
                       # lookahead for a match each time
        ))
    )++                # Iterate the above at least once, with a possessive quantifier
                       # to lock in the match (i.e. prevent any iteration from being
                       # backtracked into).
    $                  # Require that the sum equal N exactly. If this fails, the loop
                       # won't be able to find a match by backtracking, because
                       # everything inside the loop is done atomically.
)
# Assert that there are no more proper divisors of N that haven't already been summed:
(?!                # Assert that the following cannot match:
    (\2x+)         # \4 = any number greater than \2
    \4+$           # Assert \4 is a proper divisor of N
)

It isn't practical to directly port the 47 byte regex to PCRE, because it changes the value of a capture group inside the lookbehind, and upon the return of any subroutine in PCRE, all capture groups are reset to the values they had upon entering the subroutine. It is possible, however, and I did this for the corresponding Abundant numbers regex by changing the main loop from iteration into recursion.
Regex (ECMAScript), 53 bytes – Even perfect numbers
If it is ever proved that there are no odd perfect numbers, the following would be a robust answer, but for now it is noncompeting, as it only matches even perfect numbers. It was written by Grimmy on 2019-03-15.
^(?=(x(x*?))(\1\1)+$)((x*)(?=\5$))+(?!(xx+)\6+$)\1\2$

Try it online!
This uses the fact that every even perfect number is of the form \$2^{n-1} (2^n-1)\$ where \$2^n-1\$ is prime. For \$2^n-1\$ to be prime, it is necessary that \$n\$ itself be prime, so the regex does not need to do a \$log_2\$ test on \$2^n\$ to verify \$n\$ is prime. (I have used \$n\$ here instead of \$p\$ to make that clear.)
^                     # N = tail = input
(?=(x(x*?))(\1\1)+$)  # Assert that the largest odd divisor of N is >= 3 (due to
                      # having a "+" here instead of "*"; this is not necessary, but
                      # speeds up the regex's non-match when N is a a power of 2);
                      # \1 = N / {largest odd divisor of N}
                      #    == {largest power of 2 divisor of N}; \2 = \1 - 1
((x*)(?=\5$))+        # tail = N / {largest power of 2 divisor of N}
                      #      == {largest odd divisor of N}
(?!(xx+)\6+$)         # Assert tail is prime
\1\2$                 # Assert tail == \1*2 - 1; if this fails to match, it will
                      # backtrack into the "((x*)(?=\5$))+" loop (effectively
                      # multiplying by 2 repeatedly), but this will always fail
                      # to match because every subsequent match attempt will be
                      # an even number, and "\1\2$" can only be odd.

Pyth, 9 13 bytes
qsf!%QTSt

Try it online!
Thank you to the commentors for the golf help
Finds all the factors of the input, sums them, and compares that to the original input.

A few golfs for you - q0 can be replaced with !, and SQ produces the range [1-Q], so the range [1-Q) can be generated using StQ. As the Qs are now at the end of the program they can both be omitted. Fettled version, 9 bytes - qsf!%QTSt

C (gcc), 41 bytes
f(n,i,s){for(i=s=n;--i;s-=n%i?0:i);n=!s;}

Try it online!
1: 0
12: 0
13: 0
18: 0
20: 0
1000: 0
33550335: 0
6: 1
28: 1
496: 1
8128: 1
33550336: 1
-65536: 0 <---- Unable to represent final test case with four bytes, fails

Let me know if that failure for the final case is an issue.

41 bytes

"Output can be two distinct and consistent values through any allowed output format." You're not returning any two distinct values.

@OlivierGrégoire Fortunately that can be readily fixed by replacing the space with an exclamation mark!

@Neil Better yet, it can be fixed with n=!s; instead of return!s; to save 5 bytes.

@OlivierGrégoire ahh, I forgot that point. Also, updated the with the improved code. I tried something similar, but the idiot I am I did s=s which more than likely got optimized out.

Forth (gforth), 45 bytes
: f 0 over 1 ?do over i mod 0= i * - loop = ;

Try it online!
Explanation
Loops over every number from 1 to n-1, summing all values that divide n perfectly. Returns true if sum equals n
Code Explanation
: f                \ start word definition
  0 over 1         \ create a value to hold the sum and setup the bounds of the loop
  ?do              \ start a counted loop from 1 to n. (?do skips if start = end)
    over           \ copy n to the top of the stack
    i mod 0=       \ check if i divides n perfectly
    i * -          \ if so, use the fact that -1 = true in forth to add i to the sum
  loop             \ end the counted loop
  =                \ check if the sum and n are equal
;                  \ end the word definition

Husk, 5 bytes
=¹ΣhḊ

Yields 1 for a perfect argument and 0 otherwise.
Try it online!
How?
=¹ΣhḊ - Function: integer, n   e.g. 24
    Ḋ - divisors                    [1,2,3,4,6,8,12,24]
   h  - initial items               [1,2,3,4,6,8,12]
  Σ   - sum                         36
 ¹    - first argument              24
=     - equal?                      0

Actually, 5 bytes
;÷Σ½=

Full program taking input from STDIN which prints 1 for perfect numbers and 0 otherwise.
Try it online!
How?
;÷Σ½= - full program, implicitly place input, n, onto the stack
;     - duplicate top of stack
 ÷    - divisors (including n)
  Σ   - sum
   ½  - halve
    = - equal?

MathGolf, 5 4 bytes
─╡Σ=

Try it online!
Explanation
─       get divisors (includes the number itself)
 ╡      discard from right of list (removes the number itself)
  Σ     sum(list)
   =    pop(a, b), push(a==b)

Since MathGolf returns divisors rather than proper divisors, the solution is 1 byte longer than it would have been in that case.

Kotlin, 38 bytes
{i->(1..i-1).filter{i%it==0}.sum()==i}

Expanded:
{ i -> (1..i - 1).filter { i % it == 0 }.sum() == i }

Explanation:
  {i->                      start lambda with parameter i
    (2..i-1)                create a range from 2 until i - 1 (potential divisors)
      .filter{i%it==0}      it's a divisor if remainder = 0; filter divisors
        .sum()              add all divisors
          ==i                compare to the original number
             }               end lambda

Try it online!

Qbasic, 53 47 bytes
Thank you @Deadcode, -6 bytes
INPUT n
FOR i=1TO n-1
s=s-i*(n\i=n/i)
NEXT
?n=s

Literally quite Basic... Prints -1 for perfect numbers, 0 otherwise.
REPL

IF n\i=n/i THEN s=s+i can be changed to s=s-i*(n\i=n/i) for -6 bytes.

MATL, 5 bytes
EGZ\s=

Try it out at MATL Online
Explanation
      % Implicitly grab the input as an integer
      %    STACK: { 6 }
E     % Multiply by two
      %    STACK: { 12 }
G     % Grab the input again
      %    STACK: { 12,  6 }
Z\    % Compute all divisors (including itself)
      %    STACK: { 12,  [1, 2, 3, 6] }
s     % Sum up these divisors
      %    STACK: { 12, 12 }
=     % Check that the two elements on the stack are equal
      %    STACK: { 1 }
      % Implicitly display the result

VDM-SL, 101 bytes
f(i)==s([x|x in set {1,...,i-1}&i mod x=0])=i;s:seq of nat+>nat
s(x)==if x=[]then 0 else hd x+s(tl x)

Summing in VDM is not built in, so I need to define a function to do this across the sequences, this ends up taking up the majority of bytes
A full program to run might look like this:
functions
f:nat+>bool
f(i)==s([x|x in set {1,...,i-1}&i mod x=0])=i;s:seq of nat+>nat
s(x)==if x=[]then 0 else hd x+s(tl x)

Gaia, 4 bytes
dΣ⁻=

Try it online!
1 for perfect, 0 for imperfect.
d	| implicit input, n, push divisors
 Σ	| take the sum
  ⁻	| subtract n
   =	| equal to n?

Machine Language (x86 32-bit), 21 bytes
is_perfect:
        mov  ecx, esi       ; copy input number into ECX
	inc  esi
        mov  ebx, esi       ; copy input number + 1 into EBX
fact_loop:
        xor  edx, edx       ; clear EDX (high word for dividend)
        mov  eax, esi
        div  ecx            ; divide (n+1) / ECX
        dec  edx            ; if remainder = 1, n/ECX is zero and ECX is not 1
	                    ; In case EAX = 2^32-1, this doesn't work as intended
	                    ; (get edx=0 for all ECXs)
                            ; According to test 2^(2^n)-1 up to 2^128-1 are all
	                    ; deficient so it happens to be correct
        jnz  cont_loop      ; if not, continue loop
        sub  ebx, eax       ; Subtract divisor from EBX; this simultaneously
                            ; changes the running total, and sets the flags for
                            ; the return value.
        jc   done           ; If CF=1, the sum of divisors subtracted from EBX has
                            ; exceeded our input number, meaning it is abundant,
                            ; and we need to exit the loop now to return this
                            ; result in the flags.
cont_loop:
        loop fact_loop
	dec  ebx            ; Subtract the 1 added into esi for ZF
done:                       ; return value:
                            ; (JZ)  = ZF=1 = perfect
                            ; (JNZ) = ZF=0 = imperfect
end_is_perfect:

Try it online!
Machine Language (x86 32-bit), 22 bytes edited from here with also abundant? output
is_perfect:
        mov  ecx, esi       ; copy input number into ECX
        mov  ebx, esi       ; copy input number into EBX
fact_loop:
        xor  edx, edx       ; clear EDX (high word for dividend)
        lea  eax, [esi+1]
        div  ecx            ; divide (n+1) / ECX
        dec  edx            ; if remainder = 1, n/ECX is zero and ECX is not 1
	                    ; In case EAX = 2^32-1, this doesn't work as intended
	                    ; (get edx=0 for all ECXs)
                            ; According to test 2^(2^n)-1 up to 2^128-1 are all
	                    ; deficient so it happens to be correct
        jnz  cont_loop      ; if not, continue loop
        sub  ebx, eax       ; Subtract divisor from EBX; this simultaneously
                            ; changes the running total, and sets the flags for
                            ; the return value.
        jc   done           ; If CF=1, the sum of divisors subtracted from EBX has
                            ; exceeded our input number, meaning it is abundant,
                            ; and we need to exit the loop now to return this
                            ; result in the flags.
cont_loop:
        loop fact_loop
	test ebx, ebx       ; Not SUBed in last cycle
done:                       ; return value:
                            ; (JZ)  = ZF=1 = perfect
                            ; (JNZ) = ZF=0 = imperfect
                            ; (JC)  = CF=1 = abundant
                            ; (JNC) = CF=0 = non-abundant
                            ; (JA)  = CF=0 and ZF=0 = deficient
                            ; (JNA) = CF=1 or  ZF=1 = non-deficient
end_is_perfect:

Try it online!
Bad luck in last round sub is not runned and a test is needed

Nicely done! You forgot to remove the std instruction from the test harness after removing stosb, though.

Machine Language (x86 32-bit), 24 23 bytes
This is a macro that takes an unsigned integer in EAX as input (N), and outputs the result in the Zero Flag (set=perfect, clear=imperfect). It also outputs a result in the Carry Flag (set=abundant, clear=non-abundant). As such, it's actually an answer for both this question and An abundance of integers!, requiring no modification.
It's based on 640KB's abundant numbers answer, with the two additional optimizations from l4m2's answer, where the divisors are subtracted from N instead of added to it (thus simultaneously setting the flags for the return value). An input of 1 is correctly handled, costing 3 2 bytes.
89 C1 89 C3 9E EB 0E 31 D2 50 F7 F1 58 85 D2 75 04 29 CB 72 02 E2 F0

Try it online!
89 C1              mov  ecx, eax     ; copy input number into ECX
89 C3              mov  ebx, eax     ; copy input number into EBX
9E                 sahf              ; in case input number is 1, let ZF=0 and CF=0
EB 0E              jmp  cont_loop    ; exclude original number as a divisor
       fact_loop:
31 D2              xor  edx, edx     ; clear EDX (high word for dividend)
50                 push eax          ; save original dividend
F7 F1              div  ecx          ; divide EDX:EAX / ECX
58                 pop  eax          ; restore dividend
85 D2              test edx, edx     ; if remainder = 0, it divides evenly
75 04              jnz  cont_loop    ; if not, continue loop
29 CB              sub  ebx, ecx     ; Subtract divisor from EBX; this simultaneously
                                     ; changes the running total, and sets the flags for
                                     ; the return value.
72 02              jc   done         ; If CF=1, the sum of divisors subtracted from EBX has
                                     ; exceeded our input number, meaning it is abundant,
                                     ; and we need to exit the loop now to return this
                                     ; result in the flags.
       cont_loop:
E2 F0              loop fact_loop
       done:                         ; return value:
                                     ; (JZ)  = ZF=1 = perfect
                                     ; (JNZ) = ZF=0 = imperfect
                                     ; (JC)  = CF=1 = abundant
                                     ; (JNC) = CF=0 = non-abundant
                                     ; (JA)  = CF=0 and ZF=0 = deficient
                                     ; (JNA) = CF=1 or  ZF=1 = non-deficient

22 bytes

@l4m2 That's a great way to do abundant numbers with full 32-bit input support costing just 2 bytes, but as I know you know, this is perfect numbers :-)

Hmm didn't go up to check question

Is there any case where the difference happens to be times of 2^32?

@l4m2 I checked a couple days ago, generating all aliquot sums up to 2^32-1, and there are none where σ(n) = n + k*2^32. Nevertheless, I didn't want to remove my jc instruction even though it'd save 2 bytes, because I really like having a single routine that handles both abundant and perfect numbers, and also would work with any operand size (not just 32 bits)... but yep, you have a point, technically I could drop 2 bytes this way. But that would be like hard-coding the perfect numbers rather than detecting them.

Vyxal,  3+1 3 bytes
∆K=

Try it Online!
-1 byte thanks to @Deadcode for telling me to not be an idiot and remember that I added a sum of proper divisors built-in. I actually forgot that I did
This is: does the sum of the proper divisors of the input (∆K) equal the input itself (=).

Why not ∆K=? Also, this challenge requires 1 to return falsey, but your program is throwing an exception on an input of 1.

Cool, you fixed the handling of 1 :-)

@deadcode only because I was lucky enough to be reminded that I added a built-in lol ;p

Fig, \$3\log_{256}(96)\approx\$ 2.469 bytes
=Sk

Try it online!
=Sk
 S  # Does the sum
  k # Of the divisors
=   # Equal the input?

Prolog (SWI), 70 67 63 bytes
-3 bytes thanks to @Jo King
-4 bytes thanks to @Steffan
$N:-bagof(M,(between(1,N,M),N mod M<1),L),sumlist(L,S),S=:=2*N.

Try it online!

Qi, 72 bytes (45 characters + 22 brackets)
(λ(n)((☯(~> △(pass(~>(/ n _)integer?))+(= n)))(cdr(range n 0 -1))))

PowerShell, 84 bytes
param($n)1..[math]::sqrt($n)|%{$a+=$_,($n/$_)*!($n%$_)};$n-eq(($a|gu)-join"+"|iex)/2

Try it online!

Javascript, 62
n=>n==[...Array(n).keys()].filter(a=>n%a<1).reduce((a,b)=>a+b)

Explanation (although it's pretty simple)
n=> //return function that takes n
  n== //and returns if n is equal to
    [...Array(n).keys()] //an array [0..(n-1)]...
      .filter(a=>n%a<1) //where all of the elements that are not divisors of n are taken out...
      .reduce((a,b)=>a+b) //summed up

Thanks to Jo King for the improvement!

CJam, 17 bytes
ri_,(;{1$\%!},:+=

Try it online!

05AB1E, 4 bytes
Ñ¨OQ

Try it online!
Explanation
  O    # the sum
Ñ      # of the divisors of the input
 ¨     # with the last one removed
   Q   # equals the input

Batch, 81 bytes
@set s=-%1
@for /l %%i in (1,1,%1)do @set/as+=%%i*!(%1%%%%i)
@if %s%==%1 echo 1

Takes n as a command-line parameter and outputs 1 if it is a perfect number. Brute force method, starts the sum at -n so that it can include n itself in the loop.

Charcoal, 13 bytes
Ｎθ⁼θΣΦθ∧ι¬﹪θι

Try it online! Link is to verbose version of code. Outputs - for perfect numbers. Uses brute force. Explanation:
Ｎθ              Numeric input
     Φθ         Filter on implicit range
        ι       Current value (is non-zero)
       ∧        Logical And
           θ    Input value
          ﹪     Modulo
            ι   Current value
         ¬      Is zero
    Σ           Sum of matching values
  ⁼             Equals
   θ            Input value

Wolfram Language (Mathematica), 14 bytes
PerfectNumberQ

Try it online!

Yes, Mathematica! Another built-in.

Pyth, 8 bytes
qs{*MPyP

Try it online here.
qs{*MPyPQQ   Implicit: Q=eval(input())
             Trailing QQ inferred
       PQ    Prime factors of Q
      y      Powerset
     P       Remove last element - this will always be the full prime factorisation
   *M        Take product of each
  {          Deduplicate
 s           Sum
q        Q   Is the above equal to Q? Implicit print

Retina 0.8.2, 44 40 bytes
crossed out 44 is still regular 44
.+
$*
M!&`(.+)$(?<=^\1+)
1>`¶

^(1+)¶\1$

Try it online! Edit: Saved 4 bytes thanks to @Deadcode. Still somewhat slow, so link excludes largest test cases. Explanation:
.+
$*

Convert to unary.
M!&`(.+)$(?<=^\1+)

Match all factors of the input. This uses overlapping mode, which in Retina 0.8.2 requires all of the matches to start at different positions, so the matches are actually returned in descending order, starting with the original input.
1>`¶

Delete all of the newlines except for the first. This sums together the proper factors, leaving the original input and sum of factors.
^(1+)¶\1$

Test whether they are the same.

You can get -4 bytes by modifying Martin Ender's abundant numbers answer.

Powershell, 46 bytes 43 bytes
param($i)1..$i|%{$o+=$_*!($i%$_)};$o-eq2*$i

Try it Online!
Edit:
-3 bytes thanks to @AdmBorkBork

43 bytes by rolling the accumulator into the loop and checking against 2*$i to eliminate the subtract-one parens.

Java 8, 66 bytes

Someone has to use the stream API at some point, even if there's a shorter way to do it
n->java.util.stream.IntStream.range(1,n).filter(i->n%i<1).sum()==n

Try it online!

cQuents, 8 bytes
?#N=U\zN

Try it online!
Explanation
?           Mode query: return whether or not input is in sequence
 #          Conditional: iterate N, add N to sequence if condition is true
  N=         Condition: N ==
    U    )                   sum(                  )
     \z )                        proper_divisors( )
       N                                         N
        ))    implicit

Smalltalk, 34 bytes
((1to:n-1)select:[:i|n\\i=0])sum=n

Factor, 57 bytes
: f ( x -- ? ) dup [1,b) [ dupd divisor? ] filter sum = ;

Try it online!
There is a shorter solution on the Rosetta Code that uses the divisors builtin:
: f ( n -- ? )  [ divisors sum ] [ 2 * ] bi = ;

but I wanted to come up with my own solution.

Racket, 54 bytes
(require math)(define(p n)(=(sum(divisors n))(* 2 n)))

Try it online!

APL(NARS), chars 11, bytes 22
{⍵=2÷⍨11π⍵}

11π return the sum of all divisors, test:
  f←{⍵=2÷⍨11π⍵}
  f¨1 12 13 18 20 1000 33550335
0 0 0 0 0 0 0
  f¨6 28 496 8128 33550336 8589869056
1 1 1 1 1 1

MMIX, 48 bytes (12 instrs)
(jxd¹ -T)
00000000: 27010001 f7010000 e3020000 1eff0001  '¢¡¢ẋ¢¡¡ẉ£¡¡œ”¡¢
00000010: feff0006 72ffff01 220202ff 27010101  “”¡©r””¢"££”'¢¢¢
00000020: 5b01fffb 32000002 73000001 f8010000  [¢”»2¡¡£s¡¡¢ẏ¢¡¡

Disassembly:
perf    SUBU $1,$0,1        // i = n - 1
        PUT  rD,0           // zero out hidiv register
        SETL $2,0           // s = 0
0H      DIVU $255,$0,$1     // loop: t:rR = rD:n /% i
        GET  $255,rR        // t = rR
        ZSZ  $255,$255,$1   // t = t? 0 : i
        ADDU $2,$2,$255     // s += i
        SUBU $1,$1,1        // i--
        PBNZ $1,0B          // iflikely(i) goto loop
        CMPU $0,$0,$2       // r = n <=> s
        ZSZ  $0,$0,1        // r = !r
        POP  1,0            // return r

The best part of this is that the only branch in the whole thing is to do a loop. Testing the division is handled by the ZSZ instruction (which is also used later to handle a negation).
I could save an instruction if I were allowed to return different results for abundant and deficient numbers.

jxd is a program I wrote, available on request; it is like xxd, but with support for only jelly and 05AB1E codepages (and no -p or -r support, it would be redundant).

Arturo, 25 24 bytes
$=>[=∑chop factors<=&]

Try it

Thunno D, \$ 5 \log_{256}(96) \approx \$ 4.12 bytes
fZHS=

Attempt This Online!
Explanation
fZHS=  # D flag duplicates input
f      # Factors, including 1 and input
 ZH    # Without last item (input)
   S   # Sum this list
    =  # Equal to the input?
       # Implicit output

Pip, 16 bytes
a=$+Y{!\a%a}FI,a

Attempt This Online!
There's no factors builtin so we have to implement it ourselves
a=$+Y{!\a%a}FI,a    ; input on command line
              ,a    ; range(input)
     {     }FI      ; filtered by:
       \a%a         ;  the input mod this number
      !             ;  logical not
  $+Y               ; sum of the list
a=                  ; equals the input?

Pyt, 5 bytes
ĐðƩ₂=

Try it online!
Returns True if perfect, False otherwise
Đ             implicit input; Đuplicate
 ð            get ðivisors
  Ʃ           Ʃum
   ₂          divide by two
    =         is it equal to the input; implicit print

Stax, 6 bytes
ôxⁿ♪σ▀

Run and debug it
This is PackedStax, which unpacks to the following 7 bytes:
:dNs|+=

Run and debug it
Explanation
:d      # divisors
  N     # minus the last item (which is equal to the input)
    |+  # summed
      = # is equal to
   s    # the input

Times out for larger test cases on the online interpreter.

Bash, 56 bytes
Maybe a recursive function will be shorter? My Bash is certainly not strong.
s=0;for ((;v++<$1;)){ $[s+=($1%$v<1)*$v-1];};[ $s = $1 ]

A full program taking a command line argument which has a return code 0 if it was perfect and 1 otherwise.
Try it online!

Haskell, 35 bytes
f x=x==sum[y|y<-[1..x-1],x`mod`y<1]

Try it online!

Twig, 108 bytes
Yeah, I managed to get something longer than Java :/
This creates a macro that you import into your own template and call the method a
{%macro a(n,x=0)%}{%if n>1%}{%for i in 1..n-1%}{%set x=x+i*(n%i==0)%}{%endfor%}{{x==n}}{%endif%}{%endmacro%}

Returns 1 for perfect numbers, nothing for imperfect.
You can try it on https://twigfiddle.com/0or03v (testcases included)

MACHINE LANGUAGE(X86, 32 bit), 38 bytes
00000788  53                push ebx
00000789  8B442408          mov eax,[esp+0x8]
0000078D  31DB              xor ebx,ebx
0000078F  31C9              xor ecx,ecx
00000791  43                inc ebx
00000792  39C3              cmp ebx,eax
00000794  730E              jnc 0x7a4
00000796  31D2              xor edx,edx
00000798  50                push eax
00000799  F7F3              div ebx
0000079B  58                pop eax
0000079C  09D2              or edx,edx
0000079E  75F1              jnz 0x791
000007A0  01D9              add ecx,ebx
000007A2  EBED              jmp short 0x791
000007A4  29C8              sub eax,ecx
000007A6  0F94C0            setz al
000007A9  0FB6C0            movzx eax,al
000007AC  5B                pop ebx
000007AD  C3                ret
000007AE

Function lenght: 7AEh-788h=26h=38d; below assembly file that generate obj for linking:
; nasmw -fobj  this.asm
; bcc32 -v  file.c this.obj
section _DATA use32 public class=DATA
global _sumd
section _TEXT use32 public class=CODE

_sumd:
      push    ebx
      mov     eax,  dword[esp+  8]
      xor     ebx,  ebx
      xor     ecx,  ecx
.1:   inc     ebx
      cmp     ebx,  eax
      jae     .2
      xor     edx,  edx
      push    eax
      div     ebx
      pop     eax
      or      edx,  edx
      jnz     .1
      add     ecx,  ebx
      jmp     short  .1
.2:   sub     eax,  ecx
      setz    al
      movzx   eax,  al
      pop     ebx
      ret

below the C file for link and test that function:
#include <stdio.h>
unsigned es[]={1,12,13,18,20,1000,33550335,6,28,496,8128,33550336,0};
int sumd(unsigned);

main(void)
{int  i;
 for(i=0;es[i];++i)
    printf("f(%u)=%u\n",es[i],sumd(es[i]));
 return 0;
}

below the macro assembly file source of the .asm one:
; nasmw -fobj  this.asm
; bcc32 -v  file.c this.obj
section _DATA use32 public class=DATA
global  _sumd
section _TEXT use32 public class=CODE

_sumd:
<b  |a=^8|b^=b|c^=c
.1:  ++b |b>=a#.2|r^=r|<a|div b|>a|r|=r|jnz .1|c+=b|#.1
.2:  a-=c|setz al|movzx eax, al
>b
ret

below the 113 bytes golfing code of above:
_sumd:<b|a=^8|b^=b|c^=c|.1:++b|b>=a#.2|r^=r|<a|div b|>a|r|=r|jnz .1|c+=b|#.1|.2:a-=c|setz al|movzx eax, al|>b|ret

the results:
f(1)=0
f(12)=0
f(13)=0
f(18)=0
f(20)=0
f(1000)=0
f(33550335)=0
f(6)=1
f(28)=1
f(496)=1
f(8128)=1
f(33550336)=1

I got the trick of use push eax|div | pop eax from https://codegolf.stackexchange.com/a/181515/58988

C++ (clang), 55 bytes
[](long n){long i,s;for(i=s=n;--i;n%i?:s-=i);return!s;}

Try it online!
Can handle 8589869056 just fine thanks to the use of long, but this input is not included in the TIO as it takes more than 60 seconds to process. (On my i7-4930K @ 4.1 GHz it takes 65 seconds.)
This has a golf optimization that can't be done in C – n%i?:s-=i, using not only the GNU extension of allowing the true or false field of a ternary to be empty, but taking advantage of the fact that in C++, ?: and -= have the same operator precedence with right-to-left associativity – whereas in C ?: has a higher precedence than -=, so the only way to make it work would be as n%i?:(s-=i) and then it'd cost 1 byte over s-=n%i?0:i rather than saving 1 byte.

Raku, 24 bytes
{$_==sum grep $_%%*,^$_}

Try it online!

Desmos, 45 bytes

f(n)=\{\sum_{N=1}^nN\{\mod(n,N)=0,0\}=2n,0\}

\$f(n)\$ returns \$1\$ if \$n\$ is a perfect number, \$0\$ is \$n\$ is an imperfect number.
Try It On Desmos!
Try It On Desmos! - Prettified

Alice, 17 bytes
/O\B!d v
@I/-?+&<

Try it online!
Outputs 0 if the number is perfect, non 0 if imperfect
Flattened
/I\B!d&+?-/O@
/I\             Reads the input
   B            Get all the divisors in ascending order
    !           Remove the input number and save it on the tape
     d&+        Sum all the remaining divisors of the stack
        ?       Get the input from the tape
         -      Subtract from the sum
          /O@   Output and exit

APL (Dyalog/GNU), 18 bytes, ⎕IO=0
{⍵=+/(0=⍵|⍨⍳⍵)/⍳⍵}

Try it online!
(The in-browser version of APL has too small a workspace to handle the larger test cases.)
Explanation:

{...} creates an anonymous function (dfn), whose argument becomes the parameter ⍵. We'll examine the expression inside the braces from right to left.

⍳N generates a vector of the first N numbers starting with the index origin. Since that is set to 0, it goes up to N-1. For this function to work we only need to go up to N/2, but ⍳ only accepts integers, and making the division result an integer would take more characters.
/ with two vector operands produces a new vector with each element of the right vector repeated a number of times equal to the corresponding element of the left vector. In this case we're going to build a vector of N 0s and 1s with 1s only at the positions corresponding to the proper divisors of N; the result will therefore be a vector containing only those divisors.
The expression inside parentheses builds the selector; going right to left again, we start with another ⍳⍵. (We could have built a DRYer version that didn't need to repeat that, but it would have taken more characters.)
| is the divide-and-take-remainder operator (mod). By itself it takes its operands on the opposite order from e.g. C's %, but we are transposing it with ⍨ to flip that, which removes the need for another pair of parentheses. ⍵|⍨⍳⍵ divides each number from 0 to ⍵-1 into ⍵ and returns a vector of the remainders. (Fortunately for this use, 0|N just returns N rather than throwing a division-by-zero error.)
0= is what it looks like: a simple equality test with 0. Applied to a vector, it returns a vector with 1's wherever the original vector had a 0, and 0's everywhere else - exactly what we want for our selector.
Having built our vector of the proper divisors, we then compute the sum of its elements with +/ (reduction via addition).
Finally, ⍵= returns our desired result: 1 if the sum is equal to ⍵ itself, 0 otheriwse.

Scala, 72 71 bytes
saved 1 byte thanks to the comment.
Try it online!
def f(n:Int):Boolean={val F=for(i<-1 until n if n%i<1)yield i;F.sum==n}

Thunno 2, 4 bytes
FṫS=

Attempt This Online!
Explanation
FṫS=  # Implicit input     ->  28
F     # Factors of input   ->  [1,2,4,7,14,28]
 ṫ    # Without the tail   ->  [1,2,4,7,14]
  S   # Sum the list       ->  28
   =  # Equals the input?  ->  1
      # Implicit output

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes